diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 5a83bd5..0000000 Binary files a/.DS_Store and /dev/null differ diff --git a/.classpath b/.classpath deleted file mode 100644 index 51a8bba..0000000 --- a/.classpath +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/.gitignore b/.gitignore index ab31b20..84238f2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,13 @@ *.class +.DS_Store +.classpath +.project +*.iml +out/ +*.json +/.idea/* +/.idea/ +/out/*.class +target/ +.idea/ \ No newline at end of file diff --git a/.project b/.project deleted file mode 100644 index cd4a1be..0000000 --- a/.project +++ /dev/null @@ -1,17 +0,0 @@ - - - AmazonProblems - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index 3a21537..0000000 --- a/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,11 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.8 -org.eclipse.jdt.core.compiler.debug.lineNumber=generate -org.eclipse.jdt.core.compiler.debug.localVariable=generate -org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.source=1.8 diff --git a/AmazonProblems.iml b/AmazonProblems.iml deleted file mode 100644 index ea1542a..0000000 --- a/AmazonProblems.iml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..e69de29 diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..3420731 --- /dev/null +++ b/pom.xml @@ -0,0 +1,33 @@ + + + 4.0.0 + + groupId + Problems + 1.0-SNAPSHOT + + + 11 + 11 + + + + + + org.projectlombok + lombok + 1.18.24 + provided + + + + junit + junit + 4.13.2 + test + + + + \ No newline at end of file diff --git a/src/.DS_Store b/src/.DS_Store deleted file mode 100644 index 67688a5..0000000 Binary files a/src/.DS_Store and /dev/null differ diff --git a/src/geeksforgeeks/AircraftOptimization.java b/src/geeksforgeeks/AircraftOptimization.java deleted file mode 100644 index ad49b7f..0000000 --- a/src/geeksforgeeks/AircraftOptimization.java +++ /dev/null @@ -1,62 +0,0 @@ -package geeksforgeeks; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.LinkedList; -import java.util.List; - -/** - * https://leetcode.com/discuss/interview-question/318918/Amazon-or-Online-Assessment-2019-or-Optimal-Aircraft-Utilization - * - * https://leetcode.com/discuss/interview-question/373202 - */ -public class AircraftOptimization { - - public List> calculateOptimalRoute(final List> forwardList, - final List> returnList, int capacity) { - - Collections.sort(forwardList, (o1, o2) -> Integer.compare(o1.get(1), o2.get(1))); - Collections.sort(returnList, (o1, o2) -> Integer.compare(o1.get(1), o2.get(1))); - - int max = 0; - int i = 0; - int j = returnList.size() - 1; - - List> result = null; - while (i < forwardList.size() && j >= 0) { - int currentSum = forwardList.get(i).get(1) + returnList.get(j).get(1); - - if (currentSum > max && currentSum <= capacity) { - max = forwardList.get(i).get(1) + returnList.get(j).get(1); - // Initializing new list - result = new LinkedList<>(); - result.add(new ArrayList<>(Arrays.asList(forwardList.get(i).get(0), returnList.get(j).get(0)))); - i++; - } else if (forwardList.get(i).get(1) + returnList.get(j).get(1) == max) { - // no need to reset result list - result.add(new ArrayList<>(Arrays.asList(forwardList.get(i).get(0), returnList.get(j).get(0)))); - i++; - } else { - j--; - } - } - return result; - } - - public static void main(String[] args) { - AircraftOptimization aircraft = new AircraftOptimization(); - List> returnList = new ArrayList<>(); - returnList.add(new ArrayList(Arrays.asList(1, 2000))); - returnList.add(new ArrayList(Arrays.asList(2, 3000))); - returnList.add(new ArrayList(Arrays.asList(3, 4000))); - returnList.add(new ArrayList(Arrays.asList(4, 5000))); - List> forwardList = new ArrayList<>(); - forwardList.add(new ArrayList(Arrays.asList(1, 3000))); - forwardList.add(new ArrayList(Arrays.asList(2, 5000))); - forwardList.add(new ArrayList(Arrays.asList(3, 7000))); - forwardList.add(new ArrayList(Arrays.asList(4, 10000))); - List> calculateOptimalRoute = aircraft.calculateOptimalRoute(forwardList, returnList, 10000); - System.out.println(calculateOptimalRoute); - } -} diff --git a/src/geeksforgeeks/AlternateOddAndEvenNumbers.java b/src/geeksforgeeks/AlternateOddAndEvenNumbers.java deleted file mode 100644 index c8e4d68..0000000 --- a/src/geeksforgeeks/AlternateOddAndEvenNumbers.java +++ /dev/null @@ -1,53 +0,0 @@ -package geeksforgeeks; -// A JAVA program to put positive numbers at even indexes -// (0, 2, 4,..) and negative numbers at odd indexes (1, 3, -// 5, ..) - -class AlternateOddAndEvenNumbers { - - // The main function that rearranges elements of given - // array. It puts positive elements at even indexes (0, - // 2, ..) and negative numbers at odd indexes (1, 3, ..). - static void rearrange(int arr[], int n) { - // The following few lines are similar to partition - // process of QuickSort. The idea is to consider 0 - // as pivot and divide the array around it. - int i = -1, temp = 0; - for (int j = 0; j < n; j++) { - if (arr[j] < 0) { - i++; - temp = arr[i]; - arr[i] = arr[j]; - arr[j] = temp; - } - } - - // Now all positive numbers are at end and negative numbers at - // the beginning of array. Initialize indexes for starting point - // of positive and negative numbers to be swapped - int pos = i + 1, neg = 0; - - // Increment the negative index by 2 and positive index by 1, i.e., - // swap every alternate negative number with next positive number - while (pos < n && neg < pos && arr[neg] < 0) { - temp = arr[neg]; - arr[neg] = arr[pos]; - arr[pos] = temp; - pos++; - neg += 2; - } - } - - static void printArray(int arr[], int n) { - for (int i = 0; i < n; i++) - System.out.print(arr[i] + " "); - } - - public static void main(String[] args) { - int arr[] = {-1, 2, -3, 4, 5, 6, -7, 8, 9}; - int n = arr.length; - rearrange(arr, n); - System.out.println("Array after rearranging: "); - printArray(arr, n); - } -} diff --git a/src/geeksforgeeks/BitonicSearch.java b/src/geeksforgeeks/BitonicSearch.java deleted file mode 100644 index 7e22457..0000000 --- a/src/geeksforgeeks/BitonicSearch.java +++ /dev/null @@ -1,86 +0,0 @@ -package geeksforgeeks; - -/* A Bitonic Sequence is a sequence of numbers which is first strictly increasing then after a point strictly decreasing.*/ -public class BitonicSearch { - - static int ascendingBinarySearch(int arr[], int low, int high, int key) { - while (low <= high) { - int mid = low + (high - low) / 2; - if (arr[mid] == key) { - return mid; - } - if (arr[mid] > key) { - high = mid - 1; - } else { - low = mid + 1; - } - } - return -1; - } - - static int descendingBinarySearch(int arr[], int low, int high, int key) { - while (low <= high) { - int mid = low + (high - low) / 2; - if (arr[mid] == key) { - return mid; - } - if (arr[mid] < key) { - high = mid - 1; - } else { - low = mid + 1; - } - } - return -1; - } - - // -3,1,-2,-3,-4,-5,-6 - static int findBitonicPoint(int arr[], int n, int l, int r) { - int mid = ((r + l) / 2) + l; - if (arr[mid] > arr[mid - 1] && arr[mid] > arr[mid + 1]) { - return mid; - } else { - // towards right if next number is greater - if (arr[mid] > arr[mid - 1] && arr[mid] < arr[mid + 1]) { - mid = findBitonicPoint(arr, n, mid, r); - } else { - // towards left if next number is smaller - if (arr[mid] < arr[mid - 1] && arr[mid] > arr[mid + 1]) { - mid = findBitonicPoint(arr, n, l, mid); - } - } - } - return mid; - } - - static int searchBitonic(int arr[], int n, int key, int index) { - if (key > arr[index]) { - return -1; - } else if (key == arr[index]) { - return index; - } else { - int temp = ascendingBinarySearch(arr, 0, index - 1, key); - if (temp != -1) { - return temp; - } - return descendingBinarySearch(arr, index + 1, n - 1, key); - } - } - - public static void main(String args[]) { - int arr[] = {-3, 3, 9, 8, 20, 17, 5, 3, 1}; - int key = 3; - int n = arr.length; - int l = 0; - int r = n - 1; - int index = findBitonicPoint(arr, n, l, r); - - int x = searchBitonic(arr, n, key, index); - - if (x == -1) { - System.out.println("Element Not Found"); - } else { - System.out.println("Element Found at index " + x); - } - - } -} \ No newline at end of file diff --git a/src/geeksforgeeks/BuyAndSellStockAtMostTwice.java b/src/geeksforgeeks/BuyAndSellStockAtMostTwice.java deleted file mode 100644 index 88c31ec..0000000 --- a/src/geeksforgeeks/BuyAndSellStockAtMostTwice.java +++ /dev/null @@ -1,59 +0,0 @@ -package geeksforgeeks; - -/** - * https://www.geeksforgeeks.org/maximum-profit-by-buying-and-selling-a-share-at-most-twice/ - */ -class BuyAndSellStockAtMostTwice { - // { 2, 30, 15, 10, 8, 25, 80 }; - // 78,72,72,72,72,55 - // - static int maxProfit(int price[], int n) { - - int profit[] = new int[n]; - /* - * Get the maximum profit with only one transaction allowed. After this loop, - * profit[i] contains maximum profit from price[i..n-1] using at most one trans. - */ - int max_price = price[n - 1]; - for (int i = n - 2; i >= 0; i--) { - // max_price has maximum of price[i..n-1] - if (price[i] > max_price) - max_price = price[i]; - - // we can get profit[i] by taking maximum of: - // a) previous maximum, i.e., profit[i+1] - // b) profit by buying at price[i] and selling at - // max_price - profit[i] = Math.max(profit[i + 1], max_price - price[i]); - } - - /* - * Get the maximum profit with two transactions allowed After this loop, - * profit[n-1] contains the result - */ - int min_price = price[0]; - for (int i = 1; i < n; i++) { - // min_price is minimum price in price[0..i] - if (price[i] < min_price) - min_price = price[i]; - - // Maximum profit is maximum of: - // a) previous maximum, i.e., profit[i-1] - // b) (Buy, Sell) at (min_price, price[i]) and add - // profit of other trans. stored in profit[i] - profit[i] = Math.max(profit[i - 1], profit[i] + (price[i] - min_price)); - - //1->28+72 - // - - } - return profit[n - 1]; - } - - public static void main(String args[]) { - int price[] = { 2, 30, 15, 10, 8, 25, 80 }; - int n = price.length; - System.out.println("Maximum Profit = " + maxProfit(price, n)); - } - -} diff --git a/src/geeksforgeeks/Candy.java b/src/geeksforgeeks/Candy.java deleted file mode 100644 index 2bf6b7d..0000000 --- a/src/geeksforgeeks/Candy.java +++ /dev/null @@ -1,44 +0,0 @@ -package geeksforgeeks; - -import java.util.Arrays; - -/** - * https://www.hackerrank.com/challenges/candies/problem - */ -class Candy { - - // 2, 4, 2, 6, 1, 7, 8, 3, 2, 1 - // 1, 2, 1, 2, 1, 2, 3, 1, 1, 1 - // 1,2,4,3,2,1 - int candy(int[] ratings) { - int size = ratings.length; - if (size <= 1) - return size; - - int[] num = new int[size]; - Arrays.fill(num, 1); - // left to righ - for (int i = 1; i < size; i++) { - if (ratings[i] > ratings[i - 1]) - num[i] = num[i - 1] + 1; - } - - // right to left - for (int i = size - 1; i > 0; i--) { - if (ratings[i - 1] > ratings[i]) - num[i - 1] = Math.max(num[i] + 1, num[i - 1]); - } - int result = 0; - for (int i = 0; i < size; i++) { - result += num[i]; - } - return result; - } - - public static void main(String[] args) { - Candy candy = new Candy(); - - int[] ratings = {2, 4, 2, 6, 1, 7, 8, 9, 2, 1}; - System.out.println(candy.candy(ratings)); - } -} \ No newline at end of file diff --git a/src/geeksforgeeks/ClosestNumbers.java b/src/geeksforgeeks/ClosestNumbers.java deleted file mode 100644 index e9f4efc..0000000 --- a/src/geeksforgeeks/ClosestNumbers.java +++ /dev/null @@ -1,33 +0,0 @@ -package geeksforgeeks; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; - -/** - * https://www.geeksforgeeks.org/closest-numbers-list-unsorted-integers/ - */ -public class ClosestNumbers { - - public static void main(String[] args) { - //10, 50, 12, 100 - int[] arr = new int[]{5, 4, 3, 2}; - Arrays.sort(arr); - - Map map = new HashMap<>(); - - for (int i = 0; i < arr.length - 1; i++) { - map.put(arr[i] + "-" + arr[i + 1], arr[i + 1] - arr[i]); - } - int min = arr[1] - arr[0]; - for (Map.Entry m : map.entrySet()) { - if (min > m.getValue()) - min = m.getValue(); - } - - for (Map.Entry ma : map.entrySet()) { - if (ma.getValue().equals(min)) - System.out.println(ma.getKey()); - } - } -} diff --git a/src/geeksforgeeks/CompareVersions.java b/src/geeksforgeeks/CompareVersions.java deleted file mode 100644 index 344f946..0000000 --- a/src/geeksforgeeks/CompareVersions.java +++ /dev/null @@ -1,32 +0,0 @@ -package geeksforgeeks; - -/** - * https://leetcode.com/problems/compare-version-numbers/ - * - */ -public class CompareVersions { - - public static void main(String[] args) { - String str1 = "1.3.4"; - String str2 = "1.3.1.7"; - System.out.println(compareVersion(str1, str2)); - } - - public static int compareVersion(String version1, String version2) { - String[] levels1 = version1.split("\\."); - String[] levels2 = version2.split("\\."); - - int length = Math.max(levels1.length, levels2.length); - for (int i = 0; i < length; i++) { - Integer v1 = i < levels1.length ? Integer.parseInt(levels1[i]) : 0; - Integer v2 = i < levels2.length ? Integer.parseInt(levels2[i]) : 0; - int compare = v1.compareTo(v2); - if (compare != 0) { - return compare; - } - } - - return 0; - } - -} diff --git a/src/geeksforgeeks/ConstructBSTFromPreorder.java b/src/geeksforgeeks/ConstructBSTFromPreorder.java deleted file mode 100644 index 951d6aa..0000000 --- a/src/geeksforgeeks/ConstructBSTFromPreorder.java +++ /dev/null @@ -1,49 +0,0 @@ -package geeksforgeeks; - -import java.util.Stack; - -class ConstructBSTFromPreorder { - - public static void main(String[] args) { - int[] arr = {8, 3, 1, 6, 4, 7, 10, 14, 13}; - bstFromPreorder(arr); - } - - - public static TreeNode bstFromPreorder(int[] preorder) { - if (preorder == null || preorder.length == 0) { - return null; - } - Stack stack = new Stack<>(); - TreeNode root = new TreeNode(preorder[0]); - stack.push(root); - for (int i = 1; i < preorder.length; i++) { - TreeNode node = new TreeNode(preorder[i]); - if (preorder[i] < stack.peek().val) { - stack.peek().left = node; - } else { - TreeNode parent = stack.peek(); - while (!stack.isEmpty() && preorder[i] > stack.peek().val) { - parent = stack.pop(); - } - parent.right = node; - } - stack.push(node); - } - return root; - } -} - -class TreeNode { - TreeNode left; - TreeNode right; - int val; - - public TreeNode(int val) { - this.val = val; - } - - public String toString() { - return val + ""; - } -} \ No newline at end of file diff --git a/src/geeksforgeeks/CountDistinctKSubString.java b/src/geeksforgeeks/CountDistinctKSubString.java deleted file mode 100644 index 57ab9ab..0000000 --- a/src/geeksforgeeks/CountDistinctKSubString.java +++ /dev/null @@ -1,44 +0,0 @@ -package geeksforgeeks; - -// Java program to CountKSubStr number of substrings -// with exactly distinct characters in a given string - -import java.util.Arrays; - -/*https://www.geeksforgeeks.org/count-number-of-substrings-with-exactly-k-distinct-characters/*/ -public class CountDistinctKSubString { - - private int countKDist(String str, int k) { - - int result = 0; - - int n = str.length(); - - int count[] = new int[26]; - - for (int i = 0; i < n; i++) { - int distinctCount = 0; - - Arrays.fill(count, 0); - - for (int j = i; j < n; j++) { - - if (count[str.charAt(j) - 'a'] == 0) - distinctCount++; - - count[str.charAt(j) - 'a']++; - - if (distinctCount == k) - result++; - } - } - return result; - } - - public static void main(String[] args) { - CountDistinctKSubString ob = new CountDistinctKSubString(); - String ch = "pqpqs"; - int k = 2; - System.out.println("Total substrings with exactly " + k + " distinct characters : " + ob.countKDist(ch, k)); - } -} diff --git a/src/geeksforgeeks/CountingInversion.java b/src/geeksforgeeks/CountingInversion.java deleted file mode 100644 index b381ec3..0000000 --- a/src/geeksforgeeks/CountingInversion.java +++ /dev/null @@ -1,59 +0,0 @@ -package geeksforgeeks; - -/** - * https://www.geeksforgeeks.org/counting-inversions/ - * - */ -class CountingInversion { - - static int mergeSort(int[] arr, int arrSize) { - int[] temp = new int[arrSize]; - return mergeSort(arr, temp, 0, arrSize - 1); - } - - static int mergeSort(int[] arr, int[] temp, int left, int right) { - int mid; - int invCount = 0; - if (left < right) { - mid = ((right - left) / 2) + left; - - invCount = mergeSort(arr, temp, left, mid); - invCount += mergeSort(arr, temp, mid + 1, right); - - invCount += merge(arr, temp, left, mid + 1, right); - } - return invCount; - } - - static int merge(int[] arr, int[] temp, int left, int mid, int right) { - int invCount = 0; - - int i = left; - int j = mid; - int k = left; - while ((i <= mid - 1) && (j <= right)) { - if (arr[i] <= arr[j]) { - temp[k++] = arr[i++]; - } else { - temp[k++] = arr[j++]; - - invCount = invCount + (mid - i); - } - } - - while (i <= mid - 1) - temp[k++] = arr[i++]; - while (j <= right) - temp[k++] = arr[j++]; - - for (i = left; i <= right; i++) - arr[i] = temp[i]; - - return invCount; - } - - public static void main(String[] args) { - int arr[] = new int[] { 4, 6, 2, 1, 9, 7 }; - System.out.println("Number of inversions are " + mergeSort(arr, arr.length)); - } -} diff --git a/src/geeksforgeeks/FindMinimumInRotatedArray.java b/src/geeksforgeeks/FindMinimumInRotatedArray.java deleted file mode 100644 index cafb09c..0000000 --- a/src/geeksforgeeks/FindMinimumInRotatedArray.java +++ /dev/null @@ -1,32 +0,0 @@ -package geeksforgeeks; - -/*https://leetcode.com/problems/find-minimum-in-rotated-sorted-array/*/ -public class FindMinimumInRotatedArray { - - public static int findMin(int[] nums) { - if (nums.length == 0) return -1; - if (nums.length == 1) return nums[0]; - int start = 0; - int end = nums.length - 1; - while (start <= end) { - int mid = ((end - start) / 2) + start; - - if (nums[start] <= nums[end]) { - return nums[start]; - } - - if (nums[mid] < nums[end]) { - end = mid; - } else { - start = mid + 1; - } - } - return -1; - } - - - public static void main(String[] args) { - int[] arr = {7, 1, 2, 3, 4, 5, 6}; - findMin(arr); - } -} \ No newline at end of file diff --git a/src/geeksforgeeks/FindNumberOfOccurrences.java b/src/geeksforgeeks/FindNumberOfOccurrences.java deleted file mode 100644 index e4113b4..0000000 --- a/src/geeksforgeeks/FindNumberOfOccurrences.java +++ /dev/null @@ -1,43 +0,0 @@ -package geeksforgeeks; - -/*https://leetcode.com/problems/find-first-and-last-position-of-element-in-sorted-array/*/ -public class FindNumberOfOccurrences { - - public static void main(String[] args) { - int arr[] = {1, 1, 2, 2, 2, 2, 3, 3}; - System.out.println( - findLastOccurrence(arr, 0, arr.length - 1, 2) - findFirstOccurrence(arr, 0, arr.length - 1, 2) + 1); - - } - - private static int findFirstOccurrence(int[] arr, int left, int right, int key) { - if (left > right) { - return -1; - } - - int mid = (left + right) / 2; - if (arr[mid] == key && (mid == 0 || arr[mid - 1] != key)) { - return mid; - } else if (arr[mid] >= key) { - return findFirstOccurrence(arr, left, mid - 1, key); - } else { - return findFirstOccurrence(arr, mid + 1, right, key); - } - } - - private static int findLastOccurrence(int[] arr, int left, int right, int key) { - if (left > right) { - return 0; - } - - int mid = (left + right) / 2; - if (arr[mid] == key && (mid == right || arr[mid + 1] != key)) { - return mid; - } else if (arr[mid] <= key) { - return findLastOccurrence(arr, mid + 1, right, key); - } else { - return findLastOccurrence(arr, left, mid - 1, key); - } - } - -} diff --git a/src/geeksforgeeks/FindSmallestInteger.java b/src/geeksforgeeks/FindSmallestInteger.java deleted file mode 100644 index b844cc8..0000000 --- a/src/geeksforgeeks/FindSmallestInteger.java +++ /dev/null @@ -1,36 +0,0 @@ -package geeksforgeeks; - -/*https://www.geeksforgeeks.org/find-smallest-value-represented-sum-subset-given-array/*/ -class FindSmallestInteger { - - int findSmallest(int arr[], int n) { - int result = 1; - - // Traverse the array and increment 'result' if arr[i] is - // smaller than or equal to 'result'. - for (int i = 0; i < n && arr[i] <= result; i++) - result = result + arr[i]; - - return result; - } - - public static void main(String[] args) { - FindSmallestInteger small = new FindSmallestInteger(); - int arr1[] = {1, 3, 4, 5}; - int n1 = arr1.length; - System.out.println(small.findSmallest(arr1, n1)); - - int arr2[] = {1, 2, 6, 10, 11, 15}; - int n2 = arr2.length; - System.out.println(small.findSmallest(arr2, n2)); - - int arr3[] = {1, 1, 1, 1}; - int n3 = arr3.length; - System.out.println(small.findSmallest(arr3, n3)); - - int arr4[] = {1, 1, 3, 4}; - int n4 = arr4.length; - System.out.println(small.findSmallest(arr4, n4)); - - } -} \ No newline at end of file diff --git a/src/geeksforgeeks/FirstMissingPositive.java b/src/geeksforgeeks/FirstMissingPositive.java deleted file mode 100644 index 67e5dfb..0000000 --- a/src/geeksforgeeks/FirstMissingPositive.java +++ /dev/null @@ -1,56 +0,0 @@ -package geeksforgeeks; - -/** - * https://leetcode.com/problems/first-missing-positive/ - * - */ -public class FirstMissingPositive { - - public int firstMissingPositive(int[] A) { - int i = 0; - while (i < A.length) { - if (A[i] == i + 1 || A[i] <= 0 || A[i] > A.length) - i++; - else if (A[A[i] - 1] != A[i]) - swap(A, i, A[i] - 1); - else - i++; - } - i = 0; - while (i < A.length && A[i] == i + 1) - i++; - return i + 1; - } - - private void swap(int[] A, int i, int j) { - int temp = A[i]; - A[i] = A[j]; - A[j] = temp; - } - - public int firstMissingPositiveWithExtraSpace(int[] nums) { - if (nums == null || nums.length == 0) - return 1; - int length = nums.length; - int[] arr = new int[length + 1]; - for (int i = 0; i < length; i++) { - if (nums[i] <= length && nums[i] > 0) { - arr[--nums[i]] = -1; - } - } - - for (int i = 0; i < arr.length; i++) { - if (arr[i] != -1) { - return ++i; - } - } - return -1; - } - - public static void main(String[] args) { - int[] arr = { 3, 4, -1, 1 }; - FirstMissingPositive fmp = new FirstMissingPositive(); - System.out.println(fmp.firstMissingPositive(arr)); - System.out.println(fmp.firstMissingPositiveWithExtraSpace(arr)); - } -} \ No newline at end of file diff --git a/src/geeksforgeeks/FirstNonRepeatedCharacter.java b/src/geeksforgeeks/FirstNonRepeatedCharacter.java deleted file mode 100644 index 43a90e3..0000000 --- a/src/geeksforgeeks/FirstNonRepeatedCharacter.java +++ /dev/null @@ -1,24 +0,0 @@ -package geeksforgeeks; - -public class FirstNonRepeatedCharacter { - public static int firstUniqChar(String s) { - int freq[] = new int[26]; - for (int i = 0; i < s.length(); i++) - freq[s.charAt(i) - 'a']++; - // loop the same string again to find first occurrence not freq array - for (int i = 0; i < s.length(); i++) - if (freq[s.charAt(i) - 'a'] == 1) - return i; - return -1; - } - - public static void main(String args[]) { - String str = "geeksforgeeks"; - - int index = firstUniqChar(str); - if (index == -1) - System.out.print("Either all characters are " + "repeating or string is empty"); - else - System.out.print("First non-repeating character" + " is " + str.charAt(index)); - } -} diff --git a/src/geeksforgeeks/FlattenMultiLevelLinkedList.java b/src/geeksforgeeks/FlattenMultiLevelLinkedList.java deleted file mode 100644 index f054faa..0000000 --- a/src/geeksforgeeks/FlattenMultiLevelLinkedList.java +++ /dev/null @@ -1,41 +0,0 @@ -package geeksforgeeks; - -/*https://leetcode.com/problems/flatten-a-multilevel-doubly-linked-list/discuss/150321/Easy-Understanding-Java-beat-95.7-with-Explanation*/ -class FlattenMultiLevelLinkedList { - - public Node flatten(Node head) { - if (head == null) return head; - // Pointer - Node p = head; - while (p != null) { - if (p.child == null) { - p = p.next; - continue; - } - /* CASE 2: got child, find the tail of the child and link it to p.next */ - Node temp = p.child; - - while (temp.next != null) - temp = temp.next; - // Connect tail with p.next, if it is not null - temp.next = p.next; - if (p.next != null) p.next.prev = temp; - // Connect p with p.child, and remove p.child - p.next = p.child; - p.child.prev = p; - p.child = null; - } - return head; - } - - class Node { - long data; - Node next; - Node prev; - Node child; - - public Node(long id) { - this.data = id; - } - } -} \ No newline at end of file diff --git a/src/geeksforgeeks/Islands.java b/src/geeksforgeeks/Islands.java deleted file mode 100644 index 948f3bb..0000000 --- a/src/geeksforgeeks/Islands.java +++ /dev/null @@ -1,42 +0,0 @@ -package geeksforgeeks; - -/** - * https://www.geeksforgeeks.org/find-number-of-islands/ - */ -class Islands { - - private int n; - private int m; - - public int numIslands(int[][] grid) { - int count = 0; - n = grid.length; - if (n == 0) - return 0; - m = grid[0].length; - for (int i = 0; i < n; i++) { - for (int j = 0; j < m; j++) - if (grid[i][j] == 1) { - DFSMarking(grid, i, j); - ++count; - } - } - return count; - } - - private void DFSMarking(int[][] grid, int i, int j) { - if (i < 0 || j < 0 || i >= n || j >= m || grid[i][j] != 1) - return; - grid[i][j] = 0; - DFSMarking(grid, i + 1, j); - DFSMarking(grid, i - 1, j); - DFSMarking(grid, i, j + 1); - DFSMarking(grid, i, j - 1); - } - - public static void main(String[] args) { - int M[][] = new int[][]{{1, 1, 1, 1, 0}, {1, 1, 0, 1, 0}, {1, 1, 0, 0, 0}, {0, 0, 0, 0, 0}}; - Islands I = new Islands(); - System.out.println("Number of islands is: " + I.numIslands(M)); - } -} diff --git a/src/geeksforgeeks/IsomorphicArray.java b/src/geeksforgeeks/IsomorphicArray.java deleted file mode 100644 index f0ded5e..0000000 --- a/src/geeksforgeeks/IsomorphicArray.java +++ /dev/null @@ -1,48 +0,0 @@ -package geeksforgeeks; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class IsomorphicArray { - public Collection> groupIsomorphicStrings(List strings) { - if (strings == null || strings.isEmpty()) - return Collections.EMPTY_LIST; - - Map> hashToList = new HashMap<>(); - - for (String string : strings) { - String hash = hash(string); - - if (!hashToList.containsKey(hash)) - hashToList.put(hash, new ArrayList<>()); - - hashToList.get(hash).add(string); - } - return hashToList.values(); - } - - private String hash(String s) { - if (s.isEmpty()) - return ""; - - int count = 1; - StringBuilder hash = new StringBuilder(); - - Map map = new HashMap<>(); - - for (char c : s.toCharArray()) { - - if (map.containsKey(c)) - hash.append(map.get(c)); - else { - map.put(c, count++); - hash.append(map.get(c)); - } - } - return hash.toString(); - } -} diff --git a/src/geeksforgeeks/IsomorphicString.java b/src/geeksforgeeks/IsomorphicString.java deleted file mode 100644 index 3fae7d5..0000000 --- a/src/geeksforgeeks/IsomorphicString.java +++ /dev/null @@ -1,29 +0,0 @@ -package geeksforgeeks; - -import java.util.Arrays; - -/** - * https://www.geeksforgeeks.org/check-if-two-given-strings-are-isomorphic-to-each-other/ - */ -class IsomorphicString { - static boolean isIsomorphic(String s, String t) { - int m1[] = new int[256]; - int m2[] = new int[256]; - int n = s.length(); - for (int i = 0; i < n; ++i) { - // it checks the count of the character in the array ; - // for 'g' -> a[103] is 2 and 'd' -> a[100] is 2 - if (m1[s.charAt(i)] != m2[t.charAt(i)]) - return false; - m1[s.charAt(i)] = i + 1; - m2[t.charAt(i)] = i + 1; - } - System.out.println(Arrays.toString(m1)); - System.out.println(Arrays.toString(m2)); - return true; - } - - public static void main(String[] args) { - System.out.println(isIsomorphic("egg", "add")); - } -}; \ No newline at end of file diff --git a/src/geeksforgeeks/KthClosestOrigin.java b/src/geeksforgeeks/KthClosestOrigin.java deleted file mode 100644 index 323ebef..0000000 --- a/src/geeksforgeeks/KthClosestOrigin.java +++ /dev/null @@ -1,99 +0,0 @@ -package geeksforgeeks; - -import java.util.Arrays; -import java.util.Random; - -/** - * https://leetcode.com/problems/k-closest-points-to-origin/solution/ - * - */ -class KthClosestOrigin { - - static int[][] points = new int[3][2]; - - // quick select - public int[][] kClosest(int[][] points, int K) { - sort(0, points.length - 1, K); - return Arrays.copyOfRange(points, 0, K); - } - - public void sort(int i, int j, int K) { - if (i >= j) - return; - int k = new Random().nextInt(j - i + 1) + i; - swap(i, k); - - int mid = partition(i, j); - int leftLength = mid - i + 1; - if (K < leftLength) - sort(i, mid - 1, K); - else if (K > leftLength) - sort(mid + 1, j, K - leftLength); - } - - public int partition(int i, int j) { - int oi = i; - int pivot = dist(i); - i++; - - while (true) { - while (i < j && dist(i) < pivot) - i++; - while (i <= j && dist(j) > pivot) - j--; - if (i >= j) - break; - swap(i, j); - } - swap(oi, j); - return j; - } - - public int dist(int i) { - return points[i][0] * points[i][0] + points[i][1] * points[i][1]; - } - - public void swap(int i, int j) { - int t0 = points[i][0], t1 = points[i][1]; - points[i][0] = points[j][0]; - points[i][1] = points[j][1]; - points[j][0] = t0; - points[j][1] = t1; - } - - public int[][] kClosestOLogN(int[][] points, int K) { - int N = points.length; - int[] dists = new int[N]; - for (int i = 0; i < N; ++i) - dists[i] = distOLogN(points[i]); - - Arrays.sort(dists); - int distK = dists[K - 1]; - - int[][] ans = new int[K][2]; - int t = 0; - for (int i = 0; i < N; ++i) - if (distOLogN(points[i]) <= distK) - ans[t++] = points[i]; - return ans; - } - - public int distOLogN(int[] point) { - return point[0] * point[0] + point[1] * point[1]; - } - - public static void main(String[] args) { - KthClosestOrigin kth = new KthClosestOrigin(); - points[0][0] = 3; - points[0][1] = 3; - - points[1][0] = 5; - points[1][1] = -1; - - points[2][0] = 2; - points[2][1] = 4; - - // System.out.println(Arrays.deepToString(kth.kClosestOLogN(points, 1))); - System.out.println(Arrays.deepToString(kth.kClosest(points, 2))); - } -} diff --git a/src/geeksforgeeks/KthSmallestFromTwoSortedArrays.java b/src/geeksforgeeks/KthSmallestFromTwoSortedArrays.java deleted file mode 100644 index b27bf5d..0000000 --- a/src/geeksforgeeks/KthSmallestFromTwoSortedArrays.java +++ /dev/null @@ -1,39 +0,0 @@ -package geeksforgeeks; - -class KthSmallestFromTwoSortedArrays { - - public static int findKth(int[] A, int i, int[] B, int j, int k) { - - if ((A.length - i) > (B.length - j)) { - return findKth(B, j, A, i, k); - } - - if (i >= A.length) { - return B[j + k - 1]; - } - if (k == 1) { - return Math.min(A[i], B[j]); - } - - int aMid = Math.min(k / 2, A.length - i); - int bMid = k - aMid; - - if (A[i + aMid - 1] <= B[j + bMid - 1]) { - return findKth(A, i + aMid, B, j, k - aMid); - } - - return findKth(A, i, B, j + bMid, k - bMid); - } - - public static void main(String[] args) { - int arr1[] = {1,6,8,9,15}; - int arr2[] = {3,5,10,14,20}; - - int k = 6; - int ans = findKth(arr1, 0, arr2, 0, k); - if (ans == -1) - System.out.println("Invalid query"); - else - System.out.println(ans); - } -} diff --git a/src/geeksforgeeks/KthSmallestMatrix.java b/src/geeksforgeeks/KthSmallestMatrix.java deleted file mode 100644 index ac71a24..0000000 --- a/src/geeksforgeeks/KthSmallestMatrix.java +++ /dev/null @@ -1,52 +0,0 @@ -package geeksforgeeks; - -import java.util.PriorityQueue; - -/*https://www.geeksforgeeks.org/kth-smallest-element-in-a-row-wise-and-column-wise-sorted-2d-array-set-1/*/ -public class KthSmallestMatrix { - public static int kthSmallest(int[][] matrix, int k) { - int n = matrix.length; - PriorityQueue pq = new PriorityQueue<>(); - // add first row elements - for (int j = 0; j <= n - 1; j++) - pq.offer(new Points(0, j, matrix[0][j])); - for (int i = 0; i < k - 1; i++) { - Points t = pq.poll(); - System.out.println(t.x); - if (t.x == n - 1) - continue; - pq.offer(new Points(t.x + 1, t.y, matrix[t.x + 1][t.y])); - } - return pq.poll().val; - } - - public static void main(String[] args) { - int[][] matrix = {{1, 2, 9}, - {3, 11, 13}, - {4, 13, 15}}; - int k = 4; - - System.out.println(kthSmallest(matrix, k)); - } -} - -class Points implements Comparable { - int x; - int y; - int val; - - public Points(int x, int y, int val) { - this.x = x; - this.y = y; - this.val = val; - } - - @Override - public int compareTo(Points that) { - return this.val - that.val; - } - - public String toString() { - return this.val + ""; - } -} \ No newline at end of file diff --git a/src/geeksforgeeks/LRUCache.java b/src/geeksforgeeks/LRUCache.java deleted file mode 100644 index a4acbc6..0000000 --- a/src/geeksforgeeks/LRUCache.java +++ /dev/null @@ -1,132 +0,0 @@ -package geeksforgeeks; -/* - https://leetcode.com/problems/lru-cache/ - An adaption of the answer from user "liaison" on Leetcode. - Link: https://leetcode.com/problems/lru-cache/discuss/45911/Java-Hashtable-%2B-Double-linked-list-(with-a-touch-of-pseudo-nodes) - Revision by Benyam Ephrem (Dec. 31th 2018) - > Making variable names more conventional - > Adding more clarifying comments - > Moving code around to be more conventional - This code passes all Leetcode test cases as of Dec. 31st 2018 - Runtime: 77 ms, faster than 95.85% of Java online submissions for LRU Cache. - The video to explain this code is here: https://www.youtube.com/watch?v=S6IfqDXWa10 -*/ - -import java.util.HashMap; -import java.util.Map; - -class LRUCache { - - private class DNode { - int key; - int value; - DNode prev; - DNode next; - } - - private Map hashtable = new HashMap<>(); - private DNode head, tail; - private int totalItemsInCache; - private int maxCapacity; - - public LRUCache(int maxCapacity) { - - totalItemsInCache = 0; - this.maxCapacity = maxCapacity; - - head = new DNode(); - head.prev = null; - - tail = new DNode(); - tail.next = null; - - head.next = tail; - tail.prev = head; - } - - public int get(int key) { - - DNode node = hashtable.get(key); - boolean itemFoundInCache = node != null; - - if (!itemFoundInCache) { - return -1; - } - - moveToHead(node); - - return node.value; - } - - public void put(int key, int value) { - - DNode node = hashtable.get(key); - boolean itemFoundInCache = node != null; - - if (!itemFoundInCache) { - - DNode newNode = new DNode(); - newNode.key = key; - newNode.value = value; - - hashtable.put(key, newNode); - addNode(newNode); - - totalItemsInCache++; - - if (totalItemsInCache > maxCapacity) { - removeLRUEntryFromStructure(); - } - - } else { - node.value = value; - moveToHead(node); - } - } - - private void removeLRUEntryFromStructure() { - DNode tail = popTail(); - hashtable.remove(tail.key); - --totalItemsInCache; - } - - private void addNode(DNode node) { - - node.prev = head; - node.next = head.next; - - head.next.prev = node; - head.next = node; - } - - private void removeNode(DNode node) { - - DNode savedPrev = node.prev; - DNode savedNext = node.next; - - savedPrev.next = savedNext; - savedNext.prev = savedPrev; - } - - private void moveToHead(DNode node) { - removeNode(node); - addNode(node); - } - - private DNode popTail() { - DNode itemBeingRemoved = tail.prev; - removeNode(itemBeingRemoved); - return itemBeingRemoved; - } - - public static void main(String[] args) { - LRUCache lru = new LRUCache(3); - lru.put(1, 1); - lru.put(2, 2); - lru.put(3, 3); - lru.put(2, 2); - lru.put(4, 4); - lru.put(2, 2); - lru.put(3, 3); - } -} diff --git a/src/geeksforgeeks/LargestPossibleNumber.java b/src/geeksforgeeks/LargestPossibleNumber.java deleted file mode 100644 index 9f349c3..0000000 --- a/src/geeksforgeeks/LargestPossibleNumber.java +++ /dev/null @@ -1,39 +0,0 @@ -package geeksforgeeks; - -import java.util.Arrays; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -class LargestPossibleNumber { - public static void main(String[] args) { - int nums[] = {10, 68, 97, 9, 21, 12}; - List numbers = Arrays.asList("10", "68", "97", "9", "21", "12"); - - Collections.sort(numbers, (a, b) -> (b + a).compareTo(a + b)); - numbers.stream().forEach(System.out::print); - System.out.println(new LargestPossibleNumber().largestNumber(nums)); - } - - public String largestNumber(int[] nums) { - String[] arr = new String[nums.length]; - for (int i = 0; i < nums.length; i++) { - arr[i] = String.valueOf(nums[i]); - } - - Arrays.sort(arr, - (a, b) -> { - return (b + a).compareTo(a + b); - }); - - StringBuilder sb = new StringBuilder(); - for (String s : arr) { - sb.append(s); - } - - while (sb.charAt(0) == '0' && sb.length() > 1) - sb.deleteCharAt(0); - - return sb.toString(); - } -} \ No newline at end of file diff --git a/src/geeksforgeeks/LargestSubArray.java b/src/geeksforgeeks/LargestSubArray.java deleted file mode 100644 index 6abddb7..0000000 --- a/src/geeksforgeeks/LargestSubArray.java +++ /dev/null @@ -1,53 +0,0 @@ -package geeksforgeeks; - -import java.util.HashMap; - -/*https://www.geeksforgeeks.org/largest-subarray-with-equal-number-of-0s-and-1s/*/ -class LargestSubArray { - - int maxLen(int arr[], int n) { - - HashMap map = new HashMap<>(); - - int sum = 0; - int maxLength = 0; - int endingIndex = -1; - - for (int i = 0; i < n; i++) { - arr[i] = (arr[i] == 0) ? -1 : 1; - } - - for (int i = 0; i < n; i++) { - sum += arr[i]; - if (sum == 0) { - maxLength = i + 1; - endingIndex = i; - } - - if (map.containsKey(sum)) { - if (maxLength < i - map.get(sum)) { - maxLength = i - map.get(sum); - endingIndex = i; - } - } else - map.put(sum, i); - } - - for (int i = 0; i < n; i++) { - arr[i] = (arr[i] == -1) ? 0 : 1; - } - - int start = endingIndex - maxLength + 1; - System.out.println(start + " to " + endingIndex); - - return maxLength; - } - - public static void main(String[] args) { - LargestSubArray sub = new LargestSubArray(); - int arr[] = {0, 0, 0, 1, 0, 1, 1}; - int n = arr.length; - - sub.maxLen(arr, n); - } -} diff --git a/src/geeksforgeeks/LongestSpanWithSameSumArray.java b/src/geeksforgeeks/LongestSpanWithSameSumArray.java deleted file mode 100644 index 71fe75b..0000000 --- a/src/geeksforgeeks/LongestSpanWithSameSumArray.java +++ /dev/null @@ -1,44 +0,0 @@ -package geeksforgeeks; - -import java.util.HashMap; - -/*https://www.geeksforgeeks.org/longest-span-sum-two-binary-arrays/*/ -class LongestSpanWithSameSumArray { - - static int longestCommonSum(int[] arr1, int[] arr2, int n) { - int[] arr = new int[n]; - for (int i = 0; i < n; i++) - arr[i] = arr1[i] - arr2[i]; - - HashMap map = new HashMap<>(); - - int sum = 0; - int maxLength = 0; - - for (int i = 0; i < n; i++) { - - sum += arr[i]; - - - if (sum == 0) - maxLength = i + 1; - - if (map.containsKey(sum)) - maxLength = Math.max(maxLength, i - map.get(sum)); - - else - map.put(sum, i); - } - return maxLength; - } - - public static void main(String args[]) { - /* int[] arr1 = {0, 1, 0, 1, 1, 1, 1}; - int[] arr2 = {1, 1, 1, 1, 1, 0, 1};*/ - int arr1[] = {0, 1, 0, 0, 1, 1, 1, 0}; - int arr2[] = {1, 1, 1, 1, 1, 1, 0, 1}; - //{-1,0,-1,0,0,1,0} - int n = arr1.length; - System.out.println(longestCommonSum(arr1, arr2, n)); - } -} \ No newline at end of file diff --git a/src/geeksforgeeks/LongestUniqueSubstring.java b/src/geeksforgeeks/LongestUniqueSubstring.java deleted file mode 100644 index 289e2b0..0000000 --- a/src/geeksforgeeks/LongestUniqueSubstring.java +++ /dev/null @@ -1,35 +0,0 @@ -package geeksforgeeks; - -import java.util.HashMap; -import java.util.Map; - -/*https://leetcode.com/problems/longest-substring-without-repeating-characters/*/ -public class LongestUniqueSubstring { - public static int lengthOfLongestSubstring(String s) { - Map map = new HashMap<>(); - int begin = 0; - int end = 0; - int counter = 0; - int result = 0; - - while (end < s.length()) { - char c = s.charAt(end); - map.put(c, map.getOrDefault(c, 0) + 1); - if (map.get(c) > 1) counter++; - end++; - - while (counter > 0) { - char charTemp = s.charAt(begin); - if (map.get(charTemp) > 1) counter--; - map.put(charTemp, map.get(charTemp) - 1); - begin++; - } - result = Math.max(result, end - begin); - } - return result; - } - - public static void main(String[] args) { - System.out.println(lengthOfLongestSubstring("abccbcbb")); - } -} \ No newline at end of file diff --git a/src/geeksforgeeks/MatrixRowWithMax1.java b/src/geeksforgeeks/MatrixRowWithMax1.java deleted file mode 100644 index c71f828..0000000 --- a/src/geeksforgeeks/MatrixRowWithMax1.java +++ /dev/null @@ -1,48 +0,0 @@ -package geeksforgeeks; - -/** - * https://www.geeksforgeeks.org/find-the-row-with-maximum-number-1s/ - */ -public class MatrixRowWithMax1 { - - public static void main(String[] args) { - int[][] mat = {{0, 0, 0, 1}, {0, 1, 1, 1}, {1, 1, 1, 1}, {0, 0, 0, 0}}; - System.out.println(rowWithMax1s(mat)); - } - - static int rowWithMax1s(int mat[][]) { - - int R = mat.length; - int C = mat[0].length; - int max_row_index = 0; - - int j = findFirstIndex(mat[0], 0, C - 1); - if (j == -1) - j = C - 1; - - for (int i = 1; i < R; i++) { - while (j >= 0 && mat[i][j] == 1) { - j = j - 1; - max_row_index = i; - } - } - return max_row_index; - } - - static int findFirstIndex(int arr[], int low, int high) { - if (high >= low) { - int mid = low + (high - low) / 2; - - if ((mid == 0 || (arr[mid - 1] == 0)) && arr[mid] == 1) - return mid; - - else if (arr[mid] == 0) - return findFirstIndex(arr, (mid + 1), high); - - else - return findFirstIndex(arr, low, (mid - 1)); - } - return -1; - } - -} diff --git a/src/geeksforgeeks/MaximumDifference.java b/src/geeksforgeeks/MaximumDifference.java deleted file mode 100644 index f5e8c00..0000000 --- a/src/geeksforgeeks/MaximumDifference.java +++ /dev/null @@ -1,35 +0,0 @@ -package geeksforgeeks; - -/** - * https://www.geeksforgeeks.org/maximum-difference-between-two-elements/ - */ -class MaximumDifference { - - static int maxDiff(int arr[], int n) { - - int diff = arr[1] - arr[0]; - int previousSum = diff; - int maxSum = diff; - - for (int i = 1; i < n - 1; i++) { - - diff = arr[i + 1] - arr[i]; - - if (previousSum > 0) - previousSum += diff; - else - previousSum = diff; - - if (previousSum > maxSum) - maxSum = previousSum; - } - return maxSum; - } - - public static void main(String[] args) { - int arr[] = {2, 4, 1, 3, 10, 8, 5}; - int n = arr.length; - - System.out.print("Maximum difference is " + maxDiff(arr, n)); - } -} \ No newline at end of file diff --git a/src/geeksforgeeks/MaximumSubarray.java b/src/geeksforgeeks/MaximumSubarray.java deleted file mode 100644 index 8d7a333..0000000 --- a/src/geeksforgeeks/MaximumSubarray.java +++ /dev/null @@ -1,45 +0,0 @@ -package geeksforgeeks; - -/** - * https://www.geeksforgeeks.org/maximum-product-subarray/ - */ -public class MaximumSubarray { - - public static int maxProductSubArray(int[] A) { - if (A.length == 0) { - return 0; - } - - int maxHerePre = A[0]; - int minHerePre = A[0]; - int maxsofar = A[0]; - - for (int i = 1; i < A.length; i++) { - int maxHere = Math.max(Math.max(maxHerePre * A[i], minHerePre * A[i]), A[i]); - int minHere = Math.min(Math.min(maxHerePre * A[i], minHerePre * A[i]), A[i]); - maxsofar = Math.max(maxHere, maxsofar); - maxHerePre = maxHere; - minHerePre = minHere; - } - return maxsofar; - } - - public static int maxSumSubArray(int[] nums) { - - int maxSoFar = nums[0]; - int maxEndingHere = nums[0]; - - for (int i = 1; i < nums.length; i++) { - maxEndingHere = Math.max(maxEndingHere + nums[i], nums[i]); - maxSoFar = Math.max(maxSoFar, maxEndingHere); - } - return maxSoFar; - } - - - public static void main(String[] args) { - int arr[] = {1, -2, -3, 0, 8, 7, -2}; - System.out.println("Maximum Sub array product is " + maxSumSubArray(arr)); - } - -} diff --git a/src/geeksforgeeks/MaximumSubstringWithKDistinctChar.java b/src/geeksforgeeks/MaximumSubstringWithKDistinctChar.java deleted file mode 100644 index 12f3007..0000000 --- a/src/geeksforgeeks/MaximumSubstringWithKDistinctChar.java +++ /dev/null @@ -1,35 +0,0 @@ -package geeksforgeeks; - -import java.util.HashMap; -import java.util.Map; - -public class MaximumSubstringWithKDistinctChar { - - public static void main(String[] args) { - System.out.println(lengthOfLongestSubstringTwoDistinct("abaaaaacccddcc", 2)); - } - - public static int lengthOfLongestSubstringTwoDistinct(String s, int k) { - Map map = new HashMap<>(); - int start = 0; - int end = 0; - int counter = 0; - int length = 0; - while (end < s.length()) { - char c = s.charAt(end); - map.put(c, map.getOrDefault(c, 0) + 1); - if (map.get(c) == 1) counter++;//new char - end++; - while (counter > k) { - char cTemp = s.charAt(start); - map.put(cTemp, map.get(cTemp) - 1); - if (map.get(cTemp) == 0) { - counter--; - } - start++; - } - length = Math.max(length, end - start); - } - return length; - } -} diff --git a/src/geeksforgeeks/MaximumUnsortedSubarray.java b/src/geeksforgeeks/MaximumUnsortedSubarray.java deleted file mode 100644 index 8cc830a..0000000 --- a/src/geeksforgeeks/MaximumUnsortedSubarray.java +++ /dev/null @@ -1,73 +0,0 @@ -package geeksforgeeks; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -//https://www.interviewbit.com/problems/maximum-unsorted-subarray/# -public class MaximumUnsortedSubarray { - - public static ArrayList subarraySort(ArrayList A) { - - ArrayList list = new ArrayList<>(); - int start = -1; - int end = -1; - - // from left - for (int i = 1; i < A.size(); ++i) { - if (A.get(i) < A.get(i - 1)) { - start = i - 1; - break; - } - } - - // fully sorted - if (start == -1) { - list.add(-1); - return list; - } - - // from right - for (int i = A.size() - 2; i >= 0; --i) { - if (A.get(i) > A.get(i + 1)) { - end = i + 1; - break; - } - } - - // find min and max in the range [start, end] - int min = A.get(start); - int max = A.get(start); - for (int i = start; i <= end; ++i) { - min = Math.min(min, A.get(i)); - max = Math.max(max, A.get(i)); - } - - for (int i = 0; i < start; ++i) { - if (A.get(i) > min) { - start = i; - break; - } - } - - for (int i = A.size() - 1; i >= end + 1; --i) { - if (A.get(i) < max) { - end = i; - break; - } - } - - list.add(start); - list.add(end); - - return list; - } - - public static void main(String[] args) { - //1, 1, 10, 10, 15, 10, 15, 10,10, 15, 10, 15 - //(1, 3, 2, 4, 5))); - //4, 15, 4, 4, 15, 18, 20 - List result = subarraySort(new ArrayList<>(Arrays.asList(4, 15, 4, 4, 15, 18, 20))); - result.stream().forEach(System.out::println); - } -} diff --git a/src/geeksforgeeks/MedianOfRunningIntegers.java b/src/geeksforgeeks/MedianOfRunningIntegers.java deleted file mode 100644 index cef851b..0000000 --- a/src/geeksforgeeks/MedianOfRunningIntegers.java +++ /dev/null @@ -1,35 +0,0 @@ -package geeksforgeeks; - -import java.util.Collections; -import java.util.PriorityQueue; - -public class MedianOfRunningIntegers { - - PriorityQueue min = new PriorityQueue<>(); - PriorityQueue max = new PriorityQueue<>(Collections.reverseOrder()); - - public void addNum(int num) { - max.offer(num); - min.offer(max.poll()); - if (max.size() < min.size()) { - max.offer(min.poll()); - } - } - - public double findMedian() { - if (max.size() == min.size()) - return (max.peek() + min.peek()) / 2.0; - else - return max.peek(); - } - - public static void main(String[] args) { - MedianOfRunningIntegers median = new MedianOfRunningIntegers(); - int A[] = {5, 15, 1, 3, 2, 8, 7, 9, 10, 6, 11, 4}; - for (int num : A) { - median.addNum(num); - System.out.println(median.findMedian()); - } - } - -} diff --git a/src/geeksforgeeks/MergeIntervals.java b/src/geeksforgeeks/MergeIntervals.java deleted file mode 100644 index ec8371d..0000000 --- a/src/geeksforgeeks/MergeIntervals.java +++ /dev/null @@ -1,38 +0,0 @@ -package geeksforgeeks; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -class MergeIntervals { - - public static int[][] merge(int[][] intervals) { - List result = new ArrayList<>(); - if (intervals.length == 0 || intervals == null) return result.toArray(new int[0][]); - - Arrays.sort(intervals, (a, b) -> a[0] - b[0]); - - int start = intervals[0][0]; - int end = intervals[0][1]; - - for (int[] arr : intervals) { - System.out.println(arr[0] + "" + arr[1]); - if (arr[0] <= end) { - end = Math.max(end, arr[1]); - } else { - result.add(new int[]{start, end}); - start = arr[0]; - end = arr[1]; - } - } - result.add(new int[]{start, end}); - return result.toArray(new int[0][]); - - } - - public static void main(String[] args) { - int[][] arr = {{1, 3}, {2, 4}, {5, 7}, {6, 8}}; - //{{1, 9}, {2, 4}, {4, 7}, {6, 8}}; - System.out.println(Arrays.deepToString(merge(arr))); - } -} \ No newline at end of file diff --git a/src/geeksforgeeks/MinCostRopeConnect.java b/src/geeksforgeeks/MinCostRopeConnect.java deleted file mode 100644 index 1297fff..0000000 --- a/src/geeksforgeeks/MinCostRopeConnect.java +++ /dev/null @@ -1,27 +0,0 @@ -package geeksforgeeks; - -import java.util.PriorityQueue; - -public class MinCostRopeConnect { - - public static void main(String[] args) { - int arr[] = { 4, 3, 2, 6 }; - - MinCostRopeConnect rope = new MinCostRopeConnect(); - rope.connectRopes(arr); - } - - private void connectRopes(int[] arr) { - PriorityQueue pq = new PriorityQueue<>(); - for (int i : arr) - pq.add(i); - - while (pq.size() > 1) { - Integer remove = pq.remove(); - Integer remove2 = pq.remove(); - - System.out.println("cost" + (remove + remove2)); - pq.add(remove + remove2); - } - } -} diff --git a/src/geeksforgeeks/MinTimeRotOranges.java b/src/geeksforgeeks/MinTimeRotOranges.java deleted file mode 100644 index a020b8a..0000000 --- a/src/geeksforgeeks/MinTimeRotOranges.java +++ /dev/null @@ -1,98 +0,0 @@ -package geeksforgeeks; - -import java.util.LinkedList; -import java.util.Queue; - -/** - * https://github.com/bibhas-abhishek/projects/tree/master/MinTimeRotOranges - * https://www.geeksforgeeks.org/minimum-time-required-so-that-all-oranges-become-rotten/ - */ - -public class MinTimeRotOranges { - - private class Pair { - - private int x; - private int y; - - public Pair(int x, int y) { - this.x = x; - this.y = y; - } - - @Override - public String toString() { - return x + "" + y; - } - - } - - private boolean hasFreshOrange(int[][] grid) { - for (int i = 0; i < grid.length; i++) { - for (int j = 0; j < grid[0].length; j++) { - if (grid[i][j] == 1) - return true; - } - } - return false; - } - - private boolean isDelimiter(Pair p) { - return p.x == -1 && p.y == -1; - } - - private boolean isValidFresh(int row, int col, int[][] grid) { - int maxRow = grid.length; - int maxCol = grid[0].length; - return row >= 0 && row < maxRow && col >= 0 && col < maxCol && grid[row][col] == 1; - } - - private void rotOranges(Queue queue, Pair p, int[][] grid) { - int[] xMoves = {1, -1, 0, 0}; - int[] yMoves = {0, 0, 1, -1}; - for (int k = 0; k < xMoves.length; k++) { - int x = p.x + xMoves[k]; - int y = p.y + yMoves[k]; - if (isValidFresh(x, y, grid)) { - grid[x][y] = 2; - queue.add(new Pair(x, y)); - } - } - } - - public int findMinTime(int[][] grid) { - int result = 0; - Queue queue = new LinkedList<>(); - for (int i = 0; i < grid.length; i++) { - for (int j = 0; j < grid[0].length; j++) { - if (grid[i][j] == 2) - queue.add(new Pair(i, j)); - } - } - - if (queue.isEmpty()) - return -1; - - queue.add(new Pair(-1, -1)); - while (!queue.isEmpty()) { - Pair p = queue.poll(); - if (!isDelimiter(p)) - rotOranges(queue, p, grid); - else if (!queue.isEmpty()) { - queue.add(p); // add back delimiter - result += 1; - } - } - - if (hasFreshOrange(grid)) - return -1; - - return result; - } - - public static void main(String[] args) { - int grid[][] = {{2, 1, 0, 1, 1}, {1, 0, 2, 1, 1}, {1, 1, 1, 1, 1}}; - System.out.println(new MinTimeRotOranges().findMinTime(grid)); - } - -} \ No newline at end of file diff --git a/src/geeksforgeeks/MinimumDistanceBetweenTwoNumbers.java b/src/geeksforgeeks/MinimumDistanceBetweenTwoNumbers.java deleted file mode 100644 index 38b0d2d..0000000 --- a/src/geeksforgeeks/MinimumDistanceBetweenTwoNumbers.java +++ /dev/null @@ -1,47 +0,0 @@ -package geeksforgeeks; -/*https://www.geeksforgeeks.org/find-the-minimum-distance-between-two-numbers/*/ - -class MinimumDistanceBetweenTwoNumbers { - int minDist(int arr[], int n, int x, int y) { - - int i = 0; - int minDist = Integer.MAX_VALUE; - int prev = 0; - - // Find the first occurence of any of the two numbers (x or y) - // and store the index of this occurence in prev - for (i = 0; i < n; i++) { - if (arr[i] == x || arr[i] == y) { - prev = i; - break; - } - } - - // Traverse after the first occurrence - for (; i < n; i++) { - if (arr[i] == x || arr[i] == y) { - // If the current element matches with any of the two then - // check if current element and prev element are different - // Also check if this value is smaller than minimum distance - // so far - if (arr[prev] != arr[i] && (i - prev) < minDist) { - minDist = i - prev; - } - prev = i; - } - } - - return minDist; - } - - public static void main(String[] args) { - MinimumDistanceBetweenTwoNumbers min = new MinimumDistanceBetweenTwoNumbers(); - int arr[] = {2, 5, 3, 5, 4, 4, 2, 3}; - int n = arr.length; - int x = 3; - int y = 2; - - System.out.println("Minimum distance between " + x + " and " + y - + " is " + min.minDist(arr, n, x, y)); - } -} diff --git a/src/geeksforgeeks/MinimumIndexDistanceOfMaximumNumbers.java b/src/geeksforgeeks/MinimumIndexDistanceOfMaximumNumbers.java deleted file mode 100644 index fd3ed21..0000000 --- a/src/geeksforgeeks/MinimumIndexDistanceOfMaximumNumbers.java +++ /dev/null @@ -1,32 +0,0 @@ -package geeksforgeeks; - -/** - * https://www.geeksforgeeks.org/minimum-distance-between-two-occurrences-of-maximum/ - * - */ -class MinimumIndexDistanceOfMaximumNumbers { - - static int minDistance(int arr[], int n) { - int maximumElement = arr[0]; - int minDist = n; - int index = 0; - - for (int i = 1; i < n; i++) { - if (maximumElement == arr[i]) { - minDist = Math.min(minDist, (i - index)); - index = i; - } else if (maximumElement < arr[i]) { - maximumElement = arr[i]; - minDist = n; - index = i; - } - } - return minDist - 1; - } - - public static void main(String[] args) { - int arr[] = { 6, 3, 1, 3, 6, 5, 4, 1 }; - int n = arr.length; - System.out.print("Minimum distance = " + minDistance(arr, n)); - } -} \ No newline at end of file diff --git a/src/geeksforgeeks/MinimumStack.java b/src/geeksforgeeks/MinimumStack.java deleted file mode 100644 index 7510a6d..0000000 --- a/src/geeksforgeeks/MinimumStack.java +++ /dev/null @@ -1,87 +0,0 @@ -package geeksforgeeks; - -import java.util.*; - -class MyStack { - - Stack s; - Integer minEle; - - MyStack() { - s = new Stack(); - } - - void getMin() { - if (s.isEmpty()) - System.out.println("Stack is empty"); - else - System.out.println("Minimum Element in the " + " stack is: " + minEle); - } - - void peek() { - if (s.isEmpty()) { - System.out.println("Stack is empty "); - return; - } - - Integer t = s.peek(); - System.out.print("Top Most Element is: "); - - if (t < minEle) - System.out.println(minEle); - else - System.out.println(t); - } - - void pop() { - if (s.isEmpty()) { - System.out.println("Stack is empty"); - return; - } - - System.out.print("Top Most Element Removed: "); - Integer t = s.pop(); - - if (t < minEle) { - System.out.println(minEle); - minEle = 2 * minEle - t; - } else - System.out.println(t); - } - - void push(Integer x) { - if (s.isEmpty()) { - minEle = x; - s.push(x); - System.out.println("Number Inserted: " + x); - return; - } - - if (x < minEle) { - // x-minEle<0 - // x-minEle+x<0+x - // 2x-minEle= 1 && x <= N && y >= 1 && y <= N) - return true; - return false; - } - - // Method returns minimum step - // to reach target position - static int minStepToReachTarget(int knightPos[], int targetPos[], int N) { - // x and y direction, where a knight can move - int dx[] = { -2, -1, 1, 2, -2, -1, 1, 2 }; - int dy[] = { -1, -2, -2, -1, 1, 2, 2, 1 }; - - // queue for storing states of knight in board - Queue q = new LinkedList<>(); - - // push starting position of knight with 0 distance - q.add(new Cell(knightPos[0], knightPos[1], 0)); - - Cell t; - int x, y; - boolean visit[][] = new boolean[N + 1][N + 1]; - - // make all cell unvisited - for (int i = 1; i <= N; i++) - for (int j = 1; j <= N; j++) - visit[i][j] = false; - - // visit starting state - visit[knightPos[0]][knightPos[1]] = true; - - // loop untill we have one element in queue - while (!q.isEmpty()) { - t = q.remove(); - - // if current cell is equal to target cell, - // return its distance - if (t.x == targetPos[0] && t.y == targetPos[1]) - return t.dis; - - // loop for all reachable states - for (int i = 0; i < 8; i++) { - x = t.x + dx[i]; - y = t.y + dy[i]; - - // If reachable state is not yet visited and - // inside board, push that state into queue - if (isInside(x, y, N) && !visit[x][y]) { - visit[x][y] = true; - q.add(new Cell(x, y, t.dis + 1)); - } - } - } - return Integer.MAX_VALUE; - } - - public static void main(String[] args) { - int N = 30; - int[] knightPos = { 1, 1 }; - int[] targetPos = { 30, 30 }; - System.out.println(minStepToReachTarget(knightPos, targetPos, N)); - } -} \ No newline at end of file diff --git a/src/geeksforgeeks/MinimumSwapSortArray.java b/src/geeksforgeeks/MinimumSwapSortArray.java deleted file mode 100644 index ebea898..0000000 --- a/src/geeksforgeeks/MinimumSwapSortArray.java +++ /dev/null @@ -1,48 +0,0 @@ -package geeksforgeeks; - -import java.util.Map; -import java.util.TreeMap; - -/** - * https://www.geeksforgeeks.org/minimum-number-swaps-required-sort-array/ - * - */ -class MinimumSwapSortArray { - - public int minSwaps(int[] A, int N) { - int i = 0; - // Tree map sorts the order - Map resMap = new TreeMap<>(); - for (; i < N; i++) { - System.out.print(A[i] + " --> " + i + " "); - resMap.put(A[i], i); - - } - System.out.println(); - i = 0; - for (Map.Entry entry : resMap.entrySet()) { - System.out.print(entry.getKey() + " --> " + i + " "); - entry.setValue(i++); - } - int swap = 0; - for (i = 0; i < N;) { - System.out.println(A[i] + "--" + resMap.get(A[i]) + "--" + i); - // check if array position is same as treeMap's index - if (resMap.get(A[i]) != i) { - int temp = A[i]; - A[i] = A[resMap.get(temp)]; - A[resMap.get(temp)] = temp; - swap++; - } else { - i++; - } - } - return swap; - } - - public static void main(String[] args) { - int[] a = { 1, 5, 4, 3, 2 }; - MinimumSwapSortArray g = new MinimumSwapSortArray(); - System.out.println(g.minSwaps(a, a.length)); - } -} \ No newline at end of file diff --git a/src/geeksforgeeks/MinimumWindowSubsequence.java b/src/geeksforgeeks/MinimumWindowSubsequence.java deleted file mode 100644 index 2e4c2ca..0000000 --- a/src/geeksforgeeks/MinimumWindowSubsequence.java +++ /dev/null @@ -1,45 +0,0 @@ -package geeksforgeeks; - -import java.util.Arrays; - -/*https://www.programcreek.com/2014/01/leetcode-minimum-window-subsequence-java/*/ -// unresolved -public class MinimumWindowSubsequence { - - public static String minWindow(String S, String T) { - int m = T.length(), n = S.length(); - int[][] dp = new int[m + 1][n + 1]; - // fill first row with index values - for (int j = 0; j <= n; j++) { - dp[0][j] = j + 1; - } - // if any element is not present then it will set that row as zero so there wont - // be any valid values in last row - for (int i = 1; i <= m; i++) { - for (int j = 1; j <= n; j++) { - if (T.charAt(i - 1) == S.charAt(j - 1)) { - dp[i][j] = dp[i - 1][j - 1]; - } else { - dp[i][j] = dp[i][j - 1]; - } - } - } - - int start = 0; - int len = n + 1; - for (int j = 1; j <= n; j++) { - if (dp[m][j] != 0) { - if (j - dp[m][j] + 1 < len) { - start = dp[m][j] - 1; - len = j - dp[m][j] + 1; - } - } - } - System.out.println(Arrays.deepToString(dp)); - return len == n + 1 ? "" : S.substring(start, start + len); - } - - public static void main(String args[]) { - System.out.println(minWindow("abcdebdde", "bdd")); - } -} diff --git a/src/geeksforgeeks/MinimumWindowSubstring.java b/src/geeksforgeeks/MinimumWindowSubstring.java deleted file mode 100644 index 64030c2..0000000 --- a/src/geeksforgeeks/MinimumWindowSubstring.java +++ /dev/null @@ -1,55 +0,0 @@ -package geeksforgeeks; - -import java.util.HashMap; -import java.util.Map; - -class MinimumWindowSubstring { - - public static String minWindow(String s, String t) { - - if (s.length() == 0 || t.length() == 0) { - return ""; - } - - Map dictT = new HashMap<>(); - for (int i = 0; i < t.length(); i++) { - dictT.put(t.charAt(i), dictT.getOrDefault(t.charAt(i), 0) + 1); - } - - int required = dictT.size(); - int left = 0; - int right = 0; - int formed = 0; - Map windowCounts = new HashMap<>(); - int[] ans = {-1, 0, 0}; - - while (right < s.length()) { - char c = s.charAt(right); - windowCounts.put(c, windowCounts.getOrDefault(c, 0) + 1); - - if (dictT.containsKey(c) && windowCounts.get(c).intValue() == dictT.get(c).intValue()) { - formed++; - } - while (left <= right && formed == required) { - c = s.charAt(left); - if (ans[0] == -1 || right - left + 1 < ans[0]) { - ans[0] = right - left + 1; - ans[1] = left; - ans[2] = right; - } - windowCounts.put(c, windowCounts.get(c) - 1); - if (dictT.containsKey(c) && windowCounts.get(c).intValue() < dictT.get(c).intValue()) { - formed--; - } - left++; - } - right++; - } - return ans[0] == -1 ? "" : s.substring(ans[1], ans[2] + 1); - } - - public static void main(String[] args) { - System.out.println(minWindow("AADOBECODEBANC", "ABC")); - System.out.println(minWindow("abcdebdde", "bde")); - } -} \ No newline at end of file diff --git a/src/geeksforgeeks/NewRoadsMST.java b/src/geeksforgeeks/NewRoadsMST.java deleted file mode 100644 index 2df9c93..0000000 --- a/src/geeksforgeeks/NewRoadsMST.java +++ /dev/null @@ -1,113 +0,0 @@ -package geeksforgeeks; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class NewRoadsMST { - - public static void main(String[] args) { - Graph graph = new Graph(false); - graph.addEdge(1, 4, -1); - graph.addEdge(2, 3, -1); - graph.addEdge(4, 5, -1); - graph.addEdge(1, 2, 5); - graph.addEdge(1, 3, 10); - graph.addEdge(1, 6, 2); - graph.addEdge(5, 6, 5); - - NewRoadsMST newRoads = new NewRoadsMST(); - newRoads.mst(graph); - } - - private void mst(Graph graph) { - List edges = graph.allEdges; - EdgeComparator edgeComparator = new EdgeComparator(); - Collections.sort(edges, edgeComparator); - DisjointSet disjointSet = new DisjointSet(); - List resultEdges = new ArrayList<>(); - Collection allVertex = graph.getAllVertex(); - for (Object vertex : allVertex) { - disjointSet.makeNode((Vertex) vertex); - } - - for (Edge edge : edges) { - if (edge.weight < 0) { - if (disjointSet.unionSet(edge)) { - resultEdges.add(edge); - } - } - } - - for (Edge edge : edges) { - if (edge.weight > 0) { - if (disjointSet.unionSet(edge)) { - resultEdges.add(edge); - } - } - } - - for (Edge edge : resultEdges) { - System.out.println(edge.vertex1.data + "->" + edge.vertex2.data + "->" + edge.weight); - } - } - -} - -class EdgeComparator implements Comparator { - - @Override - public int compare(Edge e1, Edge e2) { - if (e1.weight < e2.weight) { - return -1; - } - return 1; - } -} - -class DisjointSet { - - Map allNodes = new HashMap<>(); - - protected void makeNode(Vertex vertex) { - Node node = new Node(vertex.id); - node.parent = node; - allNodes.put(vertex.id, node); - } - - public boolean unionSet(Edge edge) { - Node srcNode = allNodes.get(edge.vertex1.data); - Node destNode = allNodes.get(edge.vertex2.data); - - Node parentSrcNode = findParent(srcNode); - Node parentDestNode = findParent(destNode); - - if (parentSrcNode != parentDestNode) { - parentDestNode.parent = parentSrcNode; - return true; - } - return false; - } - - public Node findParent(Node node) { - Node parent = node.parent; - if (parent == node) { - return node; - } - return findParent(node.parent); - } -} - -class Node { - long data; - Node parent; - Node child; - - public Node(long id) { - this.data = id; - } -} diff --git a/src/geeksforgeeks/NextGreaterElement.java b/src/geeksforgeeks/NextGreaterElement.java deleted file mode 100644 index 6128470..0000000 --- a/src/geeksforgeeks/NextGreaterElement.java +++ /dev/null @@ -1,36 +0,0 @@ -package geeksforgeeks; - -import java.util.Stack; - -/** - * https://www.geeksforgeeks.org/next-greater-element/ - *

- * https://www.geeksforgeeks.org/find-next-greater-number-set-digits/ - */ -class NextGreaterElement { - - static int arr[] = {4, 5, 2, 25}; - - // 9,1,2,3,4,5,6,7 - public static void printNGE() { - Stack s = new Stack<>(); - int[] nge = new int[arr.length]; - - for (int i = arr.length - 1; i >= 0; i--) { - - while (!s.empty() && s.peek() <= arr[i]) { - s.pop(); - } - nge[i] = s.empty() ? -1 : s.peek(); - s.push(arr[i]); - - } - for (int i = 0; i < arr.length; i++) - System.out.println(arr[i] + " --> " + nge[i]); - - } - - public static void main(String[] args) { - printNGE(); - } -} diff --git a/src/geeksforgeeks/NextGreaterNumber.java b/src/geeksforgeeks/NextGreaterNumber.java deleted file mode 100644 index 7278dd9..0000000 --- a/src/geeksforgeeks/NextGreaterNumber.java +++ /dev/null @@ -1,72 +0,0 @@ -package geeksforgeeks; - -import java.util.Arrays; - -/** - * https://www.geeksforgeeks.org/find-next-greater-number-set-digits/ - *

- * https://www.ideserve.co.in/learn/next-greater-number-using-same-digits - */ -public class NextGreaterNumber { - - private void swap(int[] number, int i, int j) { - int temp = number[i]; - number[i] = number[j]; - number[j] = temp; - } - - private void sortSubarray(int[] number, int i, int j) { - // for this sub-array, all the digits would be in there reverse sorted position - while (i < j) { - swap(number, i, j); - i += 1; - j -= 1; - } - } - - public void findNextGreaterNumber(int[] number) { - int lastDigitSeen = number[number.length - 1]; - int i; - int j; - for (i = number.length - 2; i >= 0; i--) { - // if this digit is where the sorted ordering breaks - if (lastDigitSeen > number[i]) { - break; - } - lastDigitSeen = number[i]; - } - - if (i >= 0) // we found the digit breaking the sorted ordering - { - // find the next greater digit in the right sub-array from number[i+1 to end] - for (j = number.length - 1; j > i; j--) { - if (number[j] > number[i]) { - break; - } - } - - // swap digits at indices 'i' and 'j' - swap(number, i, j); - System.out.println(Arrays.toString(number)); - // sort the sub-array - number[i+1 to end] - sortSubarray(number, i + 1, number.length - 1); - } - } - - public static void main(String[] args) { - NextGreaterNumber solution = new NextGreaterNumber(); - - int[] number = {6, 9, 3, 8, 6, 5, 2}; - // 6938652 - // 6983652 - // 6982356 - // i =3 - - solution.findNextGreaterNumber(number); - - System.out.println("Next greater number is: "); - for (int i = 0; i < number.length; i++) { - System.out.print(number[i]); - } - } -} diff --git a/src/geeksforgeeks/NonDecreasingArray.java b/src/geeksforgeeks/NonDecreasingArray.java deleted file mode 100644 index b24d1f2..0000000 --- a/src/geeksforgeeks/NonDecreasingArray.java +++ /dev/null @@ -1,30 +0,0 @@ -package geeksforgeeks; - -/** - * https://leetcode.com/problems/non-decreasing-array/description/ - */ -class NonDecreasingArray { - public boolean checkPossibility(int[] nums) { - - int count = 0; - for (int i = 1; i < nums.length && count <= 1; i++) { - if (nums[i - 1] > nums[i]) { - count++; - if ((i - 2 < 0) || nums[i - 2] <= nums[i]) { - nums[i - 1] = nums[i]; - } else { - nums[i] = nums[i - 1]; - } - } - } - return count <= 1; - } - - public static void main(String[] args) { - // 1,4,2,3 - //3,4,2,3 - int[] nums = {7, 8, 2, 3}; - NonDecreasingArray nda = new NonDecreasingArray(); - System.out.println(nda.checkPossibility(nums)); - } -} diff --git a/src/geeksforgeeks/NutsAndBoltsMatch.java b/src/geeksforgeeks/NutsAndBoltsMatch.java deleted file mode 100644 index 95800bb..0000000 --- a/src/geeksforgeeks/NutsAndBoltsMatch.java +++ /dev/null @@ -1,71 +0,0 @@ -package geeksforgeeks; - -// Java program to solve nut and bolt problem using Quick Sort -// unresolved -public class NutsAndBoltsMatch { - public static void main(String[] args) { - // Nuts and bolts are represented as array of characters - char nuts[] = {'@', '#', '$', '%', '^', '&'}; - char bolts[] = {'$', '%', '&', '^', '@', '#'}; - - // Method based on quick sort which matches nuts and bolts - matchPairs(nuts, bolts, 0, 5); - - System.out.println("Matched nuts and bolts are : "); - printArray(nuts); - printArray(bolts); - } - - // Method to print the array - private static void printArray(char[] arr) { - for (char ch : arr) { - System.out.print(ch + " "); - } - System.out.print("n"); - } - - // Method which works just like quick sort - private static void matchPairs(char[] nuts, char[] bolts, int low, - int high) { - if (low < high) { - // Choose last character of bolts array for nuts partition. - int pivot = partition(nuts, low, high, bolts[high]); - - // Now using the partition of nuts choose that for bolts - // partition. - partition(bolts, low, high, nuts[pivot]); - - // Recur for [low...pivot-1] & [pivot+1...high] for nuts and - // bolts array. - matchPairs(nuts, bolts, low, pivot - 1); - matchPairs(nuts, bolts, pivot + 1, high); - } - } - - // Similar to standard partition method. Here we pass the pivot element - // too instead of choosing it inside the method. - private static int partition(char[] arr, int low, int high, char pivot) { - int i = low; - char temp1, temp2; - for (int j = low; j < high; j++) { - if (arr[j] < pivot) { - temp1 = arr[i]; - arr[i] = arr[j]; - arr[j] = temp1; - i++; - } else if (arr[j] == pivot) { - temp1 = arr[j]; - arr[j] = arr[high]; - arr[high] = temp1; - j--; - } - } - temp2 = arr[i]; - arr[i] = arr[high]; - arr[high] = temp2; - - // Return the partition index of an array based on the pivot - // element of other array. - return i; - } -} diff --git a/src/geeksforgeeks/OwnDataStructureUtil.java b/src/geeksforgeeks/OwnDataStructureUtil.java deleted file mode 100644 index 6ffe412..0000000 --- a/src/geeksforgeeks/OwnDataStructureUtil.java +++ /dev/null @@ -1,51 +0,0 @@ -package geeksforgeeks; - -import java.util.*; - -class OwnDataStructureUtil { - ArrayList list; - HashMap map; - java.util.Random rand = new java.util.Random(); - - public OwnDataStructureUtil() { - list = new ArrayList<>(); - map = new HashMap<>(); - } - - public boolean insert(int val) { - if (map.containsKey(val)) - return false; - map.put(val, list.size()); - list.add(val); - return true; - } - - public boolean remove(int val) { - if (!map.containsKey(val)) - return false; - int loc = map.get(val); - if (loc < list.size() - 1) { // not the last one than swap the last one with this val - int lastOne = list.get(list.size() - 1); - list.set(loc, lastOne); - map.put(lastOne, loc); - } - map.remove(val); - list.remove(list.size() - 1); - return true; - } - - public int getRandom() { - return list.get(rand.nextInt(list.size())); - } - - public static void main(String[] args) { - OwnDataStructureUtil ds = new OwnDataStructureUtil(); - ds.insert(10); - ds.insert(20); - ds.insert(30); - ds.insert(40); - ds.remove(20); - ds.insert(50); - System.out.println(ds.getRandom()); - } -} diff --git a/src/geeksforgeeks/PalindromeSinglyLinkedList.java b/src/geeksforgeeks/PalindromeSinglyLinkedList.java deleted file mode 100644 index 9c51c27..0000000 --- a/src/geeksforgeeks/PalindromeSinglyLinkedList.java +++ /dev/null @@ -1,61 +0,0 @@ -package geeksforgeeks; - -public class PalindromeSinglyLinkedList { - - class Node { - int data; - Node next; - - public Node(int data) { - this.data = data; - } - - } - - public static void main(String[] args) { - - PalindromeSinglyLinkedList palindrome = new PalindromeSinglyLinkedList(); - Node head = palindrome.new Node(1); - head.next = palindrome.new Node(2); - head.next.next = palindrome.new Node(3); - head.next.next.next = palindrome.new Node(2); - head.next.next.next.next = palindrome.new Node(1); - - System.out.println(palindrome.isPalindrome(head)); - - } - - public boolean isPalindrome(Node head) { - Node fast = head, slow = head; - while (fast != null && fast.next != null) { - fast = fast.next.next; - slow = slow.next; - } - if (fast != null) { // odd nodes: let right half smaller - slow = slow.next; - } - slow = reverse(slow); - fast = head; - - while (slow != null) { - if (fast.data != slow.data) { - return false; - } - fast = fast.next; - slow = slow.next; - } - return true; - } - - public Node reverse(Node head) { - Node prev = null; - while (head != null) { - Node next = head.next; - head.next = prev; - prev = head; - head = next; - } - return prev; - } - -} diff --git a/src/geeksforgeeks/PartitionLabel.java b/src/geeksforgeeks/PartitionLabel.java deleted file mode 100644 index c5f7d31..0000000 --- a/src/geeksforgeeks/PartitionLabel.java +++ /dev/null @@ -1,33 +0,0 @@ -package geeksforgeeks; - -import java.util.ArrayList; -import java.util.List; - -class PartitionLabel { - public List partitionLabels(String S) { - int[] last = new int[26]; - for (int i = 0; i < S.length(); ++i) - last[S.charAt(i) - 'a'] = i; - - int j = 0; - int anchor = 0; - List ans = new ArrayList<>(); - for (int i = 0; i < S.length(); ++i) { - j = Math.max(j, last[S.charAt(i) - 'a']); - if (i == j) { - // it is (1+i-anchor) - int length = i - anchor + 1; - ans.add(length); - anchor = i + 1; - } - } - return ans; - } - - public static void main(String[] args) { - String S = "ababcbacadefegdehijhklij"; - PartitionLabel partition = new PartitionLabel(); - System.out.println(partition.partitionLabels(S)); - - } -} \ No newline at end of file diff --git a/src/geeksforgeeks/Pattern132.java b/src/geeksforgeeks/Pattern132.java deleted file mode 100644 index e2815e5..0000000 --- a/src/geeksforgeeks/Pattern132.java +++ /dev/null @@ -1,31 +0,0 @@ -package geeksforgeeks; - -import java.util.Arrays; -/*https://leetcode.com/problems/132-pattern/discuss/94089/Java-solutions-from-O(n3)-to-O(n)-for-%22132%22-pattern-(updated-with-one-pass-slution)*/ -public class Pattern132 { - - - public static boolean find132pattern(int[] arr) { - int[] temp = Arrays.copyOf(arr, arr.length); - - for (int i = 1; i < arr.length; i++) { - temp[i] = Math.min(arr[i - 1], temp[i - 1]); - } - //{3, 3, 3, 1, 1, 1, 1, 1} - - for (int j = arr.length - 1, top = arr.length; j >= 0; j--) { - if (arr[j] <= temp[j]) continue; - while (top < arr.length && temp[top] <= temp[j]) top++; - if (top < arr.length && arr[j] > temp[top]) return true; - temp[--top] = arr[j]; - } - - return false; - } - - public static void main(String[] args) { - int[] arr = {3, 4, 1, 2, 9, 6, 7, 8}; - - find132pattern(arr); - } -} diff --git a/src/geeksforgeeks/ProductExceptSelf.java b/src/geeksforgeeks/ProductExceptSelf.java deleted file mode 100644 index 6a0306e..0000000 --- a/src/geeksforgeeks/ProductExceptSelf.java +++ /dev/null @@ -1,27 +0,0 @@ -package geeksforgeeks; - -import java.util.Arrays; - -/** - * https://leetcode.com/problems/product-of-array-except-self/ - */ -public class ProductExceptSelf { - public static int[] productExceptSelf(int[] nums) { - int n = nums.length; - int[] res = new int[n]; - res[0] = 1; - for (int i = 1; i < n; i++) { - res[i] = res[i - 1] * nums[i - 1]; - } - int right = 1; - for (int i = n - 1; i >= 0; i--) { - res[i] *= right; - right *= nums[i]; - } - return res; - } - - public static void main(String[] args) { - System.out.println(Arrays.toString(productExceptSelf(new int[]{1, 2, 3, 4, 5}))); - } -} \ No newline at end of file diff --git a/src/geeksforgeeks/RailwayPlatformProblem.java b/src/geeksforgeeks/RailwayPlatformProblem.java deleted file mode 100644 index 60fadd4..0000000 --- a/src/geeksforgeeks/RailwayPlatformProblem.java +++ /dev/null @@ -1,43 +0,0 @@ -package geeksforgeeks; - -import java.util.Arrays; - -/** - * https://www.geeksforgeeks.org/minimum-number-platforms-required-railwaybus-station/ - */ -public class RailwayPlatformProblem { - - static int findPlatform(int[] arrival, int[] departure, int n) { - Arrays.sort(arrival); - Arrays.sort(departure); - - int platNeeded = 1; - int result = 1; - int i = 1; - int j = 0; - - while (i < n && j < n) { - if (arrival[i] <= departure[j]) { - platNeeded++; - i++; - - if (platNeeded > result) - result = platNeeded; - } else { - platNeeded--; - j++; - } - } - return result; - } - - public static void main(String[] args) { - - int[] arr = {900, 940, 950, 1100, 1500, 1800}; - int[] dep = {910, 1200, 1120, 1130, 1900, 2000}; - - int n = arr.length; - System.out.println("Minimum Number of Platforms Required = " + findPlatform(arr, dep, n)); - } - -} diff --git a/src/geeksforgeeks/RandomLinkedList.java b/src/geeksforgeeks/RandomLinkedList.java deleted file mode 100644 index cd68a47..0000000 --- a/src/geeksforgeeks/RandomLinkedList.java +++ /dev/null @@ -1,60 +0,0 @@ -package geeksforgeeks; - -class LLNode { - public int val; - public LLNode next; - public LLNode random; - - public LLNode() { - } - - public LLNode(int _val, LLNode _next, LLNode _random) { - val = _val; - next = _next; - random = _random; - } - - public String toString() { - return "" + this.val; - } -}; - -class RandomLinkedList { - public LLNode copyRandomList(LLNode head) { - if (head == null) - return null; - LLNode temp = head; - while (temp != null) { - LLNode LLNode = new LLNode(temp.val + 10, temp.next, null); - temp.next = LLNode; - temp = temp.next.next; - } - LLNode randomLLNode = head; - while (randomLLNode != null) { - randomLLNode.next.random = randomLLNode.random.next; - randomLLNode = randomLLNode.next.next; - } - LLNode copyHead = head.next; - LLNode tempHead = copyHead; - while (head != null && tempHead != null) { - head.next = head.next == null || head.next.next == null ? head.next : head.next.next; - tempHead.next = tempHead.next == null || tempHead.next.next == null ? tempHead.next : tempHead.next.next; - head = head.next; - tempHead = tempHead.next; - } - return copyHead; - } - - public static void main(String[] args) { - LLNode head = new LLNode(1, null, null); - head.next = new LLNode(2, null, null); - head.next.next = new LLNode(3, null, null); - head.next.next.next = new LLNode(4, null, null); - head.random = head.next.next; - head.next.random = head.next.next.next; - head.next.next.random = head.next; - head.next.next.next.random = head; - RandomLinkedList solution = new RandomLinkedList(); - solution.copyRandomList(head); - } -} diff --git a/src/geeksforgeeks/RemoveKDigits.java b/src/geeksforgeeks/RemoveKDigits.java deleted file mode 100644 index e9b14c3..0000000 --- a/src/geeksforgeeks/RemoveKDigits.java +++ /dev/null @@ -1,47 +0,0 @@ -package geeksforgeeks; - -import java.util.Stack; - -/** - * https://leetcode.com/problems/remove-k-digits/ - */ - -public class RemoveKDigits { - //1432219 - public static String removeKdigits(String num, int k) { - int len = num.length(); - - if (k == len) - return "0"; - - Stack stack = new Stack<>(); - int i = 0; - while (i < num.length()) { - while (k > 0 && !stack.isEmpty() && stack.peek() > num.charAt(i)) { - stack.pop(); - k--; - } - stack.push(num.charAt(i)); - i++; - } - - // corner case like "1111" - while (k > 0) { - stack.pop(); - k--; - } - - StringBuilder sb = new StringBuilder(); - while (!stack.isEmpty()) - sb.append(stack.pop()); - sb.reverse(); - - while (sb.length() > 1 && sb.charAt(0) == '0') - sb.deleteCharAt(0); - return sb.toString(); - } - - public static void main(String[] args) { - System.out.println(removeKdigits("14232191", 3)); - } -} \ No newline at end of file diff --git a/src/geeksforgeeks/RestoreIPAddress.java b/src/geeksforgeeks/RestoreIPAddress.java deleted file mode 100644 index 1ac6c71..0000000 --- a/src/geeksforgeeks/RestoreIPAddress.java +++ /dev/null @@ -1,32 +0,0 @@ -package geeksforgeeks; - -import java.util.*; -//unresolved -class RestoreIPAddress { - - public List restoreIpAddresses(String s) { - List result = new ArrayList<>(); - doRestore(result, "", s, 0); - return result; - } - - private void doRestore(List result, String path, String s, int k) { - if (s.isEmpty() || k == 4) { - if (s.isEmpty() && k == 4) - result.add(path.substring(1)); - return; - } - for (int i = 1; i <= (s.charAt(0) == '0' ? 1 : 3) && i <= s.length(); i++) { // Avoid leading 0 - String part = s.substring(0, i); - if (Integer.valueOf(part) <= 255) { - System.out.println(path); - doRestore(result, path + "." + part, s.substring(i), k + 1); - } - } - } - - public static void main(String[] args) { - RestoreIPAddress ip = new RestoreIPAddress(); - System.out.println(ip.restoreIpAddresses("25525511135").toString()); - } -} diff --git a/src/geeksforgeeks/RotateMatrixInPlace.java b/src/geeksforgeeks/RotateMatrixInPlace.java deleted file mode 100644 index 950813b..0000000 --- a/src/geeksforgeeks/RotateMatrixInPlace.java +++ /dev/null @@ -1,72 +0,0 @@ -package geeksforgeeks; - -/** - * https://www.geeksforgeeks.org/inplace-rotate-square-matrix-by-90-degrees/ - */ -// looking for easy solution -class RotateMatrixInPlace { - - /** - * N/2 for time complexity o(n) - *

- * get all the corners first and swap ( rotate 90 degree) - * - * @param N - * @param mat - */ - static void rotateMatrix(int N, int mat[][]) { - // Consider all squares one by one - for (int x = 0; x < N / 2; x++) { - // Consider elements in group of 4 in - // current square - for (int y = x; y < N - x - 1; y++) { - // store current cell in temp variable - System.out.println("mat[" + x + "][" + y + "] = " + mat[x][y]); - int temp = mat[x][y]; - - // move values from right to top - System.out.println("mat[" + y + "][" + (N - 1 - x) + "] = " + mat[y][N - 1 - x]); - mat[x][y] = mat[y][N - 1 - x]; - - // move values from bottom to right - System.out.println("mat[" + (N - 1 - x) + "][" + (N - 1 - y) + "] = " + mat[N - 1 - x][N - 1 - y]); - mat[y][N - 1 - x] = mat[N - 1 - x][N - 1 - y]; - - // move values from left to bottom - System.out.println("mat[" + (N - 1 - y) + "][" + (x) + "] = " + mat[N - 1 - y][x]); - mat[N - 1 - x][N - 1 - y] = mat[N - 1 - y][x]; - - // assign temp to left - System.out.println("mat[" + (N - 1 - y) + "][" + (x) + "] = " + temp); - mat[N - 1 - y][x] = temp; - } - } - } - - static void displayMatrix(int N, int mat[][]) { - for (int i = 0; i < N; i++) { - for (int j = 0; j < N; j++) - System.out.print(" " + mat[i][j]); - - System.out.print("\n"); - } - System.out.print("\n"); - } - - public static void main(String[] args) { - int N = 4; - - // Test Case 1 - int mat[][] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}}; - - // int mat[][] = { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} }; - // int mat[][] = { {1, 2}, {4, 5} }; - - // displayMatrix(mat); - - rotateMatrix(N, mat); - - // Print rotated matrix - displayMatrix(N, mat); - } -} \ No newline at end of file diff --git a/src/geeksforgeeks/SearchAnElementInMatrix.java b/src/geeksforgeeks/SearchAnElementInMatrix.java deleted file mode 100644 index 61bd8af..0000000 --- a/src/geeksforgeeks/SearchAnElementInMatrix.java +++ /dev/null @@ -1,65 +0,0 @@ -package geeksforgeeks; -/*https://www.youtube.com/watch?v=FOa55B9Ikfg -* https://leetcode.com/problems/search-a-2d-matrix-ii/ -https://leetcode.com/problems/search-a-2d-matrix/*/ - - -public class SearchAnElementInMatrix { - - private static boolean searchII(int[][] mat, int n, int x) { - - int i = 0; - int j = n - 1; // set indexes for top right element - - while (i < n && j >= 0) { - if (mat[i][j] == x) { - System.out.print("n Found at " + i + " " + j); - return true; - } - if (mat[i][j] > x) - j--; - else // if mat[i][j] < x - i++; - } - - System.out.print("n Element not found"); - return false; - - } - - // why column : no.of.columns define the row correctly and rows wont help us to find the exact row - public static boolean searchI(int[][] matrix, int target) { - if (matrix == null || matrix.length == 0) { - return false; - } - int start = 0; - int rows = matrix.length; - int cols = matrix[0].length; - int end = rows * cols - 1; - while (start <= end) { - int mid = (start + end) / 2; - if (matrix[mid / cols][mid % cols] == target) { - return true; - } - if (matrix[mid / cols][mid % cols] < target) { - start = mid + 1; - } else { - end = mid - 1; - } - } - return false; - } - - public static void main(String[] args) { - int[][] mat = {{10, 20, 30, 40}, {15, 25, 35, 45}, {27, 29, 37, 48}, {32, 33, 39, 50}}; - - int[][] matI = {{1, 3, 5, 7}, - {10, 11, 16, 20}, - {23, 30, 34, 50}}; - - System.out.println(searchI(matI, 11)); - System.out.println(searchII(mat, 4, 33)); - - } - -} diff --git a/src/geeksforgeeks/SearchElementInSortedAndRotatedArray.java b/src/geeksforgeeks/SearchElementInSortedAndRotatedArray.java deleted file mode 100644 index 64c5714..0000000 --- a/src/geeksforgeeks/SearchElementInSortedAndRotatedArray.java +++ /dev/null @@ -1,41 +0,0 @@ -package geeksforgeeks; - -/*https://leetcode.com/problems/search-in-rotated-sorted-array/*/ -public class SearchElementInSortedAndRotatedArray { - - public static void main(String[] args) { - int[] arr = {4, 5, 6, 7, 0, 1, 2}; - SearchElementInSortedAndRotatedArray search = new SearchElementInSortedAndRotatedArray(); - System.out.println(search.searchInArray(arr, 1)); - } - - // 4, 5, 6, 7, 0, 1, 2 - int searchInArray(int arr[], int key) { - - int left = 0; - int right = arr.length - 1; - - while (left < right) { - - int mid = (left + right) / 2; - if (arr[mid] == key) - return mid; - - if (arr[left] < arr[mid]) { - if (key > arr[left] && key < arr[mid]) { - right = mid - 1; - } else { - left = mid + 1; - } - } else { - if (key > arr[mid] && key < arr[right]) { - left = mid + 1; - } else { - right = mid - 1; - } - } - } - return -1; - } - -} diff --git a/src/geeksforgeeks/SerializeAndDeserialize.java b/src/geeksforgeeks/SerializeAndDeserialize.java deleted file mode 100644 index 336a088..0000000 --- a/src/geeksforgeeks/SerializeAndDeserialize.java +++ /dev/null @@ -1,83 +0,0 @@ -package geeksforgeeks; - -import java.util.Arrays; -import java.util.LinkedList; -import java.util.Queue; - -/* - Serialize and Deserialize Binary Tree - LeetCode: - https://leetcode.com/problems/serialize-and-deserialize-binary-tree/ - - This code passes all Leetcode test cases as of April 26 2019 - Runtime: 12 ms, faster than 62.25% of Java online submissions for Serialize and Deserialize Binary Tree. - Memory Usage: 39.4 MB, less than 71.37% of Java online submissions for Serialize and Deserialize Binary Tree. - - The video to explain this code is here: https://www.youtube.com/watch?v=suj1ro8TIVY -*/ - -public class SerializeAndDeserialize { - - private static final String NULL_SYMBOL = "X"; - private static final String DELIMITER = ","; - - public String serialize(TreeNode root) { - - if (root == null) { - return NULL_SYMBOL + DELIMITER; - } - - String leftSerialized = serialize(root.left); - String rightSerialized = serialize(root.right); - - return root.val + DELIMITER + leftSerialized + rightSerialized; - } - - public TreeNode deserialize(String data) { - Queue nodesLeftToMaterialize = new LinkedList<>(); - nodesLeftToMaterialize.addAll(Arrays.asList(data.split(DELIMITER))); - return deserializeHelper(nodesLeftToMaterialize); - } - - public TreeNode deserializeHelper(Queue nodesLeftToMaterialize) { - - String valueForNode = nodesLeftToMaterialize.poll(); - - if (valueForNode.equals(NULL_SYMBOL)) { - return null; - } - - TreeNode newNode = new TreeNode(Integer.valueOf(valueForNode)); - newNode.left = deserializeHelper(nodesLeftToMaterialize); - newNode.right = deserializeHelper(nodesLeftToMaterialize); - - return newNode; - } - - public static void main(String[] args) { - TreeNode root = new TreeNode(1); - root.left = new TreeNode(2); - root.right = new TreeNode(3); - root.left.right = new TreeNode(4); - root.right.left = new TreeNode(5); - root.right.right = new TreeNode(6); - root.right.left.left = new TreeNode(7); - root.right.left.right = new TreeNode(8); - SerializeAndDeserialize sede = new SerializeAndDeserialize(); - String serialize = sede.serialize(root); - System.out.println(serialize); - TreeNode deserialze = sede.deserialize(serialize); - sede.printTree(deserialze); - } - - public void printTree(TreeNode node) { - if (node == null) { - System.out.print("X,"); - return; - } - System.out.print(node.val + ","); - printTree(node.left); - printTree(node.right); - } - -} - diff --git a/src/geeksforgeeks/SimilarExperssions.java b/src/geeksforgeeks/SimilarExperssions.java deleted file mode 100644 index 37456b9..0000000 --- a/src/geeksforgeeks/SimilarExperssions.java +++ /dev/null @@ -1,56 +0,0 @@ -package geeksforgeeks; - -/** - * https://www.geeksforgeeks.org/check-two-expressions-brackets/ - * - */ -//unresolved -public class SimilarExperssions { - - public static void main(String[] args) { - - System.out.println('+' * '-'); - - String query = "-(a+b+c)"; - String response = "-a-b-c"; - - checkExpression(query.toCharArray(), response.toCharArray()); - } - - private static void checkExpression(char[] query, char[] response) { - - char symbol = 0; - int startIndex = 0; - int endIndex = 0; - for (int i = 0; i < query.length; i++) { - if (query[i] == '(') { - symbol = query[i - 1]; - startIndex = i + 1; - } else if (query[i] == ')') { - endIndex = i - 1; - solve(symbol, startIndex, endIndex, query); - - } - } - } - - private static void solve(char symbol, int startIndex, int endIndex, char[] query) { - - for (int i = startIndex; i <= endIndex; i++) { - char temp = 0; - if (query[startIndex] == '-') { - temp = '-' * '+'; - query[i] = temp; - } else if (symbol == '+') { - temp = '-'; - query[i] = temp; - } else if (Character.isLetter(query[startIndex])) { - if (startIndex == i) { - query[startIndex - 1] = temp; - } - System.out.print(query[startIndex]); - } - System.out.print(temp); - } - } -} \ No newline at end of file diff --git a/src/geeksforgeeks/SlidingWindow.java b/src/geeksforgeeks/SlidingWindow.java deleted file mode 100644 index 0f54c68..0000000 --- a/src/geeksforgeeks/SlidingWindow.java +++ /dev/null @@ -1,36 +0,0 @@ -package geeksforgeeks; - -import java.util.Deque; -import java.util.LinkedList; - -public class SlidingWindow { - - public static void main(String[] args) { - int arr[] = {8, 5, 10, 7, 9, 4, 15, 12, 90, 13}; - int k = 3; - printMax(arr, arr.length, k); - } - - private static void printMax(int[] arr, int length, int k) { - Deque deque = new LinkedList<>(); - int i = 0; - for (; i < k; i++) { - while (!deque.isEmpty() && arr[i] >= arr[deque.peekLast()]) - deque.removeLast(); - deque.addLast(i); - } - for (; i < length; i++) { - System.out.println(arr[deque.peekFirst()]); - - while (!deque.isEmpty() && deque.peekFirst() <= i - k) - deque.removeFirst(); - - while (!deque.isEmpty() && arr[i] >= arr[deque.peekLast()]) - deque.removeLast(); - - deque.addLast(i); - } - System.out.println(arr[deque.peekFirst()]); - } - -} \ No newline at end of file diff --git a/src/geeksforgeeks/SnakeAndLadder.java b/src/geeksforgeeks/SnakeAndLadder.java deleted file mode 100644 index cc10da3..0000000 --- a/src/geeksforgeeks/SnakeAndLadder.java +++ /dev/null @@ -1,72 +0,0 @@ -package geeksforgeeks; - -import java.util.Queue; -import java.util.LinkedList; - -/** - * https://leetcode.com/problems/snakes-and-ladders/ - */ -public class SnakeAndLadder { - - static class QEntry { - int vertex; - int noOfMoves; - } - - static int getMinDiceThrows(int board[], int n) { - Queue queue = new LinkedList<>(); - QEntry qEntry = new QEntry(); - qEntry.vertex = 0; - qEntry.noOfMoves = 0; - - board[0] = -21; - queue.add(qEntry); - - while (!queue.isEmpty()) { - qEntry = queue.remove(); - int v = qEntry.vertex; - - System.out.println(v); - - if (v == n - 1) - break; - - for (int j = v + 1; j <= (v + 6) && j < n; j++) { - if (board[j] != -21) { - QEntry entry = new QEntry(); - entry.noOfMoves = (qEntry.noOfMoves + 1); - - if (board[j] != -1) - entry.vertex = board[j]; - else - entry.vertex = j; - queue.add(entry); - board[j] = -21; - } - } - } - return qEntry.noOfMoves; - } - - public static void main(String[] args) { - - int N = 30; - int moves[] = new int[N]; - for (int i = 0; i < N; i++) - moves[i] = -1; - - // Ladders - moves[2] = 21; - moves[4] = 7; - moves[10] = 25; - moves[19] = 28; - - // Snakes - moves[3] = 1; - moves[23] = 8; - moves[16] = 3; - moves[18] = 6; - - System.out.println("Min Dice throws required is " + getMinDiceThrows(moves, N)); - } -} \ No newline at end of file diff --git a/src/geeksforgeeks/SpiralMatrix.java b/src/geeksforgeeks/SpiralMatrix.java deleted file mode 100644 index c6d83cb..0000000 --- a/src/geeksforgeeks/SpiralMatrix.java +++ /dev/null @@ -1,49 +0,0 @@ -package geeksforgeeks; - -class SpiralMatrix { - - static void spiralPrint(int rowEnd, int colEnd, int a[][]) { - int rowStart = 0; - int colStart = 0; - - while (rowStart < colEnd && colStart < rowEnd) { - // Print the first row from the remaining rowEnd - for (int i = colStart; i < rowEnd; ++i) { - System.out.print(a[rowStart][i] + " "); - } - rowStart++; - - // Print the last column from the remaining colEnd - for (int i = rowStart; i < colEnd; ++i) { - System.out.print(a[i][rowEnd - 1] + " "); - } - rowEnd--; - - // Print the last row from the remaining rowEnd */ - if (rowStart < colEnd) { - for (int i = rowEnd - 1; i >= colStart; --i) { - System.out.print(a[colEnd - 1][i] + " "); - } - colEnd--; - } - - // Print the first column from the remaining colEnd */ - if (colStart < rowEnd) { - for (int i = colEnd - 1; i >= rowStart; --i) { - System.out.print(a[i][colStart] + " "); - } - colStart++; - } - } - } - - public static void main(String[] args) { - int R = 4; - int C = 4; - int a[][] = {{1, 2, 3, 4}, - {5, 6, 7, 8}, - {9, 10, 11, 12}, - {13, 14, 15, 16}}; - spiralPrint(R, C, a); - } -} \ No newline at end of file diff --git a/src/geeksforgeeks/SpiralMatrixII.java b/src/geeksforgeeks/SpiralMatrixII.java deleted file mode 100644 index 7cb1b22..0000000 --- a/src/geeksforgeeks/SpiralMatrixII.java +++ /dev/null @@ -1,56 +0,0 @@ -package geeksforgeeks; - -import java.util.Arrays; - -public class SpiralMatrixII { - - public static int[][] generateMatrix(int n) { - // Declaration - int[][] matrix = new int[n][n]; - - // Edge Case - if (n == 0) { - return matrix; - } - - - // Normal Case - int rowStart = 0; - int rowEnd = n - 1; - int colStart = 0; - int colEnd = n - 1; - int num = 1; // change - - while (rowStart <= rowEnd && colStart <= colEnd) { - for (int i = colStart; i <= colEnd; i++) { - matrix[rowStart][i] = num++; // change - } - rowStart++; - - for (int i = rowStart; i <= rowEnd; i++) { - matrix[i][colEnd] = num++; // change - } - colEnd--; - - for (int i = colEnd; i >= colStart; i--) { - if (rowStart <= rowEnd) - matrix[rowEnd][i] = num++; // change - } - rowEnd--; - - for (int i = rowEnd; i >= rowStart; i--) { - if (colStart <= colEnd) - matrix[i][colStart] = num++; // change - } - colStart++; - - System.out.println(Arrays.deepToString(matrix)); - } - - return matrix; - } - - public static void main(String[] args) { - generateMatrix(4); - } -} diff --git a/src/geeksforgeeks/SplitLinkedList.java b/src/geeksforgeeks/SplitLinkedList.java deleted file mode 100644 index fb3e4d5..0000000 --- a/src/geeksforgeeks/SplitLinkedList.java +++ /dev/null @@ -1,81 +0,0 @@ -package geeksforgeeks; - -//https://leetcode.com/problems/split-linked-list-in-parts/ -class SplitLinkedList { - public ListNode[] splitListToParts(ListNode root, int k) { - if (root == null) return null; - if (k == 0) return null; - - if (k == 1) { - ListNode[] node = new ListNode[1]; - node[0] = root; - return node; - } - - - ListNode[] node = new ListNode[k]; - int length = getRootLength(root); - - if (k > length) { - ListNode temp = root; - int index = 0; - - while (temp != null) { - ListNode result = temp; - temp = temp.next; - result.next = null; - node[index] = result; - index++; - } - - for (; index < k; index++) { - node[index] = null; - } - return node; - } - - int remainder = length % k; - int quo = length / k; - - for (int i = 0; i < k; i++) { - int value = remainder > 0 ? quo + 1 : quo; - remainder--; - ListNode head = root; - ListNode prev = null; - for (int j = 0; j < value && root != null; j++) { - if (j == value - 1) { - prev = root; - prev.next = null; - } - root = root.next; - } - node[i] = head; - } - - return node; - } - - public int getRootLength(ListNode root) { - ListNode temp = root; - int count = 0; - while (temp != null) { - count++; - temp = temp.next; - } - return count; - } - - public static void main(String[] args) { - ListNode root = new ListNode(1); - root.next = new ListNode(2); - root.next.next = new ListNode(3); - root.next.next.next = new ListNode(4); - root.next.next.next.next = new ListNode(5); - root.next.next.next.next.next = new ListNode(6); - root.next.next.next.next.next.next = new ListNode(7); - root.next.next.next.next.next.next.next = new ListNode(8); - root.next.next.next.next.next.next.next.next = new ListNode(9); - root.next.next.next.next.next.next.next.next.next = new ListNode(10); - new SplitLinkedList().splitListToParts(root, 3); - } -} diff --git a/src/geeksforgeeks/StockBuySellManyTimes.java b/src/geeksforgeeks/StockBuySellManyTimes.java deleted file mode 100644 index c4a4587..0000000 --- a/src/geeksforgeeks/StockBuySellManyTimes.java +++ /dev/null @@ -1,71 +0,0 @@ -package geeksforgeeks; - -import java.util.ArrayList; -import java.util.List; - -class Interval { - int buy, sell; -} - -/** - * https://www.geeksforgeeks.org/stock-buy-sell/ - */ -// unresolved -class StockBuySellManyTimes { - - //200, 180, 260, 310, 40, 535, 695 - void stockBuySell(int price[], int n) { - // Prices must be given for at least two days - if (n == 1) - return; - - int count = 0; - - List result = new ArrayList<>(); - - int i = 0; - while (i < n - 1) { - // Find Local Minima. Note that the limit is (n-2) as we are - // comparing present element to the next element. - while ((i < n - 1) && (price[i + 1] <= price[i])) - i++; - - // If we reached the end, break as no further solution possible - if (i == n - 1) - break; - - Interval e = new Interval(); - e.buy = i++; - // Store the index of minima - - // Find Local Maxima. Note that the limit is (n-1) as we are - // comparing to previous element - while ((i < n) && (price[i] >= price[i - 1])) - i++; - - // Store the index of maxima - e.sell = i - 1; - result.add(e); - - // Increment number of buy/sell - count++; - } - - if (count == 0) - System.out.println("There is no day when buying the stock " + "will make profit"); - else - for (int j = 0; j < count; j++) - System.out.println("Buy on day: " + result.get(j).buy + " " + "Sell on day : " + result.get(j).sell); - - return; - } - - public static void main(String args[]) { - StockBuySellManyTimes stock = new StockBuySellManyTimes(); - - int price[] = {200, 180, 260, 310, 40, 535, 695}; - int n = price.length; - - stock.stockBuySell(price, n); - } -} diff --git a/src/geeksforgeeks/SumOfThreeElements.java b/src/geeksforgeeks/SumOfThreeElements.java deleted file mode 100644 index 108825a..0000000 --- a/src/geeksforgeeks/SumOfThreeElements.java +++ /dev/null @@ -1,39 +0,0 @@ -package geeksforgeeks; - -import java.util.Arrays; - -public class SumOfThreeElements { - - public static void main(String[] args) { - int arr[] = {1, 4, 45, 6, 10, 8}; - int sum = 22; - SumOfThreeElements ste = new SumOfThreeElements(); - ste.findTriplets(arr, sum); - } - - private boolean findTriplets(int[] arr, int sum) { - - int arrSize = arr.length; - Arrays.sort(arr); - - for (int i = 0; i < arrSize; i++) { - - int left = i + 1; - int right = arrSize - 1; - - while (left < right) { - int result = arr[i] + arr[left] + arr[right]; - if (result == sum) { - System.out.println("triplet found" + i + "+" + left + "+" + right); - break; - } else if (result < sum) { - left++; - } else { - right--; - } - } - } - - return false; - } -} diff --git a/src/geeksforgeeks/SwapRecoverBST.java b/src/geeksforgeeks/SwapRecoverBST.java deleted file mode 100644 index 4de4707..0000000 --- a/src/geeksforgeeks/SwapRecoverBST.java +++ /dev/null @@ -1,44 +0,0 @@ -package geeksforgeeks; - -public class SwapRecoverBST { - - TreeNode firstElement = null; - TreeNode secondElement = null; - // The reason for this initialization is to avoid null pointer exception in the first comparison when prevElement has not been initialized - TreeNode prevElement = new TreeNode(Integer.MIN_VALUE); - - public void recoverTree(TreeNode root) { - - // In order traversal to find the two elements - traverse(root); - - // Swap the values of the two nodes - int temp = firstElement.val; - firstElement.val = secondElement.val; - secondElement.val = temp; - } - - private void traverse(TreeNode root) { - - if (root == null) - return; - - traverse(root.left); - - // Start of "do some business", - // If first element has not been found, assign it to prevElement (refer to 6 in the example above) - if (firstElement == null && prevElement.val >= root.val) { - firstElement = prevElement; - } - - // If first element is found, assign the second element to the root (refer to 2 in the example above) - if (firstElement != null && prevElement.val >= root.val) { - secondElement = root; - } - prevElement = root; - - // End of "do some business" - - traverse(root.right); - } -} \ No newline at end of file diff --git a/src/geeksforgeeks/TaskLeastInterval.java b/src/geeksforgeeks/TaskLeastInterval.java deleted file mode 100644 index 107fbad..0000000 --- a/src/geeksforgeeks/TaskLeastInterval.java +++ /dev/null @@ -1,45 +0,0 @@ -package geeksforgeeks; - -import java.util.*; - -/*https://leetcode.com/problems/task-scheduler/*/ -// unresolved -public class TaskLeastInterval { - - public static int leastInterval(char[] tasks, int n) { - Map map = new HashMap<>(); - for (int i = 0; i < tasks.length; i++) { - map.put(tasks[i], map.getOrDefault(tasks[i], 0) + 1); - } - PriorityQueue> queue = new PriorityQueue<>( - (a, b) -> Integer.compare(b.getValue(), a.getValue())); - - queue.addAll(map.entrySet()); - - int count = 0; - while (!queue.isEmpty()) { - int k = n + 1; - List tempList = new ArrayList<>(); - while (k > 0 && !queue.isEmpty()) { - Map.Entry top = queue.poll(); - top.setValue(top.getValue() - 1); - tempList.add(top); - k--; - count++; - } - - for (Map.Entry e : tempList) { - if (e.getValue() > 0) queue.add(e); // add valid tasks - } - - if (queue.isEmpty()) break; - count = count + k; // if k > 0, then it means we need to be idle - } - return count; - } - - public static void main(String[] args) { - char[] arr = "AAAAAABCDEFG".toCharArray(); - leastInterval(arr, 2); - } -} diff --git a/src/geeksforgeeks/UniquePath.java b/src/geeksforgeeks/UniquePath.java deleted file mode 100644 index c022223..0000000 --- a/src/geeksforgeeks/UniquePath.java +++ /dev/null @@ -1,64 +0,0 @@ -package geeksforgeeks; - -/*https://leetcode.com/problems/unique-paths-ii/ - https://leetcode.com/problems/unique-paths/*/ -public class UniquePath { - - public static void main(String[] args) { - System.out.println(uniquePathI(3, 2)); - - int[][] matrix = {{0, 0, 0}, - {0, 1, 0}, - {0, 0, 0}}; - - System.out.println(uniquePathII(matrix)); - - } - - private static int uniquePathI(int row, int col) { - int[][] dp = new int[row][col]; - - for (int i = 0; i < col; i++) { - dp[0][i] = 1; - } - - for (int j = 0; j < row; j++) { - dp[j][0] = 1; - } - - for (int i = 1; i < row; i++) { - for (int j = 1; j < col; j++) { - dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; - } - } - return dp[row - 1][col - 1]; - } - - - private static int uniquePathII(int[][] mat) { - int[][] dp = new int[mat.length][mat[0].length]; - for (int i = 0; i < mat[0].length; i++) { - if (mat[0][i] != 1) { - dp[0][i] = 1; - } - } - - for (int i = 0; i < mat.length; i++) { - if (mat[i][0] != 1) { - dp[i][0] = 1; - } - } - - for (int i = 1; i < mat.length; i++) { - for (int j = 1; j < mat[0].length; j++) { - if (mat[i][j] == 1) { - dp[i][j] = 0; - } else { - dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; - } - - } - } - return dp[mat.length - 1][mat[0].length - 1]; - } -} diff --git a/src/geeksforgeeks/VulgarDecimal.java b/src/geeksforgeeks/VulgarDecimal.java deleted file mode 100644 index 973665e..0000000 --- a/src/geeksforgeeks/VulgarDecimal.java +++ /dev/null @@ -1,69 +0,0 @@ -package geeksforgeeks; - -/*https://leetcode.com/problems/fraction-to-recurring-decimal/*/ - -import java.util.*; - -public class VulgarDecimal { - - public static String fractionToDecimal(long num, long den) { - if (num == 0) { - return "0"; - } - StringBuilder result = new StringBuilder(); - - result.append(((num > 0) ^ (den > 0)) ? "-" : ""); - result.append(num / den); - num %= den; - if (num == 0) { - return result.toString(); - } - - result.append("."); - HashMap map = new HashMap<>(); - map.put(num, result.length()); - while (num != 0) { - num *= 10; - result.append(num / den); - num %= den; - if (map.containsKey(num)) { - int index = map.get(num); - result.insert(index, "("); - result.append(")"); - break; - } else { - map.put(num, result.length()); - } - } - return result.toString(); - } - - - /** - * boolean doTestsPass() - * Returns true if all tests pass. Otherwise false - *

- * Consider adding more tests. - */ - public static boolean doTestsPass() { - boolean testsPassed = true; - - // testsPassed &= fractionToDecimal(1l, 2l).equals("0.5"); - //testsPassed &= fractionToDecimal(1l, 3l).equals("0.(3)"); - //testsPassed &= fractionToDecimal(1l, 30l).equals("0.0(3)"); - //testsPassed &= fractionToDecimal(1l, 75l).equals("0.01(3)"); - //testsPassed &= fractionToDecimal(4l, 7l).equals("0.(571428)"); - testsPassed &= fractionToDecimal(1l, 56l).equals("0.017(857142)"); - - if (testsPassed) { - System.out.println("Tests passes"); - } else { - System.out.println("Tests failed"); - } - return testsPassed; - } - - public static void main(String[] args) { - doTestsPass(); - } -} diff --git a/src/geeksforgeeks/WaterTrapping.java b/src/geeksforgeeks/WaterTrapping.java deleted file mode 100644 index e76f6eb..0000000 --- a/src/geeksforgeeks/WaterTrapping.java +++ /dev/null @@ -1,37 +0,0 @@ -package geeksforgeeks; - -class WaterTrapping { - - static int findWater(int arr[], int n) { - int result = 0; - int leftMax = 0; - int rightMax = 0; - int low = 0; - int high = n - 1; - - while (low < high) { - if (arr[low] < arr[high]) { - if (arr[low] > leftMax) { - leftMax = arr[low]; - } else { - result += leftMax - arr[low]; - } - low++; - } else { - if (arr[high] > rightMax) { - rightMax = arr[high]; - } else { - result += rightMax - arr[high]; - } - high--; - } - } - return result; - } - - public static void main(String[] args) { - int arr[] = {0, 1, 0, 2, 1, 0, 1, 3, 2, 1}; - int n = arr.length; - System.out.println("Maximum water that " + "can be accumulated is " + findWater(arr, n)); - } -} \ No newline at end of file diff --git a/src/main/java/RandomProblemGenerator.java b/src/main/java/RandomProblemGenerator.java new file mode 100644 index 0000000..69074d5 --- /dev/null +++ b/src/main/java/RandomProblemGenerator.java @@ -0,0 +1,35 @@ +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import java.util.Random; +import java.util.stream.Collectors; + +public class RandomProblemGenerator { + public static void main(String[] args) throws IOException { + List filesInFolder = Files.walk(Paths.get("src/main/java")) + .filter(Files::isRegularFile) + .map(Path::toFile) + .filter(file -> !(file.getPath().contains("lld") || + file.getPath().contains("java8") || + file.getPath().contains("reflections") || + file.getPath().contains("multithreading") || + file.getPath().contains("internals"))) + .filter(file -> file.getName().contains(".java")) + .collect(Collectors.toList()); + + int rand= (new Random().nextInt(filesInFolder.size())); + System.out.println(rand+" "+filesInFolder.get(rand).getName()); + try (BufferedReader reader = + new BufferedReader(new FileReader(filesInFolder.get(rand)))){ + reader.lines().filter(e->e.contains("http") || e.contains("https")).forEach(System.out::println); + } + + System.out.println("Total files: "+filesInFolder.size()); + } + +} diff --git a/src/main/java/SQL/AlterTableWithMonthName.sql b/src/main/java/SQL/AlterTableWithMonthName.sql new file mode 100644 index 0000000..8541ba7 --- /dev/null +++ b/src/main/java/SQL/AlterTableWithMonthName.sql @@ -0,0 +1,35 @@ + +--when you give query like this +--SELECT id, +--CASE WHEN month = "Jan" THEN revenue END as "Jan_Revenue", +--CASE WHEN month = "Feb" THEN revenue END AS "Feb_Revenue" +--FROM Department; +--the output will be +--+----+-------------+-------------+ +--| id | Jan_Revenue | Feb_Revenue | +--+----+-------------+-------------+ +--| 1 | NULL | 7000 | +--| 1 | 8000 | NULL | +--| 1 | NULL | NULL | +--| 2 | 9000 | NULL | +--| 3 | NULL | 10000 | +--+----+-------------+-------------+ +--To get one row for each id we need to aggregate by id using GROUP BY. +--But since we have multiple rows with the same id but different values (e.g. for id=1 we have Jan_Revenues: +-- NULL, 8000 and NULL. When we merge these 3 together what value should be chosen? +--This is why we need either SUM (NULL+8000+NULL) or MAX, in both cases 8000 will be used + +SELECT id, +MAX(CASE WHEN month='Jan' then revenue else null end) Jan_Revenue, +MAX(CASE WHEN month='Feb' then revenue else null end) Feb_Revenue, +MAX(CASE WHEN month='Mar' then revenue else null end) Mar_Revenue, +MAX(CASE WHEN month='Apr' then revenue else null end) Apr_Revenue, +MAX(CASE WHEN month='May' then revenue else null end) May_Revenue, +MAX(CASE WHEN month='Jun' then revenue else null end) Jun_Revenue, +MAX(CASE WHEN month='Jul' then revenue else null end) Jul_Revenue, +MAX(CASE WHEN month='Aug' then revenue else null end) Aug_Revenue, +MAX(CASE WHEN month='Sep' then revenue else null end) Sep_Revenue, +MAX(CASE WHEN month='Oct' then revenue else null end) Oct_Revenue, +MAX(CASE WHEN month='Nov' then revenue else null end) Nov_Revenue, +MAX(CASE WHEN month='Dec' then revenue else null end) Dec_Revenue +FROM Department GROUP BY id \ No newline at end of file diff --git a/src/main/java/SQL/ClassHavingMoreStudents.sql b/src/main/java/SQL/ClassHavingMoreStudents.sql new file mode 100644 index 0000000..86808a4 --- /dev/null +++ b/src/main/java/SQL/ClassHavingMoreStudents.sql @@ -0,0 +1,29 @@ +--+---------+------------+ +--| student | class | +--+---------+------------+ +--| A | Math | +--| B | English | +--| C | Math | +--| D | Biology | +--| E | Math | +--| F | Computer | +--| G | Math | +--| H | Math | +--| I | Math | +--+---------+------------+ + +--output: +-- +--+---------+ +--| class | +--+---------+ +--| Math | +--+---------+ + + +SELECT + class +FROM + courses +GROUP BY class +HAVING COUNT(DISTINCT student) >= 5 \ No newline at end of file diff --git a/src/main/java/SQL/CustomersNerverOrders.sql b/src/main/java/SQL/CustomersNerverOrders.sql new file mode 100644 index 0000000..1bbd733 --- /dev/null +++ b/src/main/java/SQL/CustomersNerverOrders.sql @@ -0,0 +1,24 @@ +--Suppose that a website contains two tables, the Customers table and the Orders table. Write a SQL query to find all customers who never order anything. +-- +--Table: Customers. +-- +--+----+-------+ +--| Id | Name | +--+----+-------+ +--| 1 | Joe | +--| 2 | Henry | +--| 3 | Sam | +--| 4 | Max | +--+----+-------+ +--Table: Orders. +-- +--+----+------------+ +--| Id | CustomerId | +--+----+------------+ +--| 1 | 3 | +--| 2 | 1 | +--+----+------------+ + + +select cu.Name as Customers from Customers cu left join Orders od on cu.Id=od.CustomerId +where od.CustomerId is null \ No newline at end of file diff --git a/src/main/java/SQL/DeleteDuplicate.sql b/src/main/java/SQL/DeleteDuplicate.sql new file mode 100644 index 0000000..06ea0f8 --- /dev/null +++ b/src/main/java/SQL/DeleteDuplicate.sql @@ -0,0 +1,5 @@ + + +-- the below command will do cross product and generate all combinations of rows +-- we are selecting a row with same Email and Greater Id +DELETE p1 from Person p1, Person p2 where p1.Email=p2.Email and p1.Id>p2.Id \ No newline at end of file diff --git a/src/main/java/SQL/DepartmentHighestSalary.sql b/src/main/java/SQL/DepartmentHighestSalary.sql new file mode 100644 index 0000000..2589613 --- /dev/null +++ b/src/main/java/SQL/DepartmentHighestSalary.sql @@ -0,0 +1,50 @@ + +--+----+-------+--------+--------------+ +--| Id | Name | Salary | DepartmentId | +--+----+-------+--------+--------------+ +--| 1 | Joe | 70000 | 1 | +--| 2 | Jim | 90000 | 1 | +--| 3 | Henry | 80000 | 2 | +--| 4 | Sam | 60000 | 2 | +--| 5 | Max | 90000 | 1 | +--+----+-------+--------+--------------+ + +--+----+----------+ +--| Id | Name | +--+----+----------+ +--| 1 | IT | +--| 2 | Sales | +--+----+----------+ + +--OUTPUT +--+------------+----------+--------+ +--| Department | Employee | Salary | +--+------------+----------+--------+ +--| IT | Max | 90000 | +--| IT | Jim | 90000 | +--| Sales | Henry | 80000 | +--+------------+----------+--------+ + +--this inner query will give single top salaries for each dept, however if there are multiple people who gets the same salary? we do one more join +-- SELECT d.Id +-- , MAX(d.Name) AS Name +-- , MAX(e.Salary) AS Salary +-- FROM Department d +-- JOIN Employee e +-- ON e.DepartmentId = d.Id +-- GROUP BY d.Id + +SELECT d1.Name as Department + , e1.Name as Employee + , e1.Salary +FROM ( + SELECT d.Id + , MAX(d.Name) AS Name + , MAX(e.Salary) AS Salary + FROM Department d + JOIN Employee e + ON e.DepartmentId = d.Id + GROUP BY d.Id +) d1 +JOIN Employee e1 +ON d1.Id = e1.DepartmentId AND e1.Salary = d1.Salary \ No newline at end of file diff --git a/src/main/java/SQL/DuplicateEmail.sql b/src/main/java/SQL/DuplicateEmail.sql new file mode 100644 index 0000000..b635a55 --- /dev/null +++ b/src/main/java/SQL/DuplicateEmail.sql @@ -0,0 +1,20 @@ +--Write a SQL query to find all duplicate emails in a table named Person. +--+----+---------+ +--| Id | Email | +--+----+---------+ +--| 1 | a@b.com | +--| 2 | c@d.com | +--| 3 | a@b.com | +--+----+---------+ + +select distinct(t1.Email) as Email +from Person t1 +where 1<( + select count(t2.Email) + from Person t2 where t1.Email=t2.Email) + + -- Inner Join concept +select distinct p1.Email +from Person p1 +inner join Person p2 on p1.Email=P2.Email +where p1.Id <> p2.Id \ No newline at end of file diff --git a/src/main/java/SQL/EmployeeEarningHigherThanManagers.sql b/src/main/java/SQL/EmployeeEarningHigherThanManagers.sql new file mode 100644 index 0000000..f912bae --- /dev/null +++ b/src/main/java/SQL/EmployeeEarningHigherThanManagers.sql @@ -0,0 +1,3 @@ + + +select tb1.Name as Employee from Employee tb1 inner join Employee tb2 on tb1.ManagerId=tb2.Id where tb1.Salary>tb2.Salary diff --git a/src/main/java/SQL/ExchangeSeats.sql b/src/main/java/SQL/ExchangeSeats.sql new file mode 100644 index 0000000..3e425ec --- /dev/null +++ b/src/main/java/SQL/ExchangeSeats.sql @@ -0,0 +1,27 @@ +--Mary is a teacher in a middle school and she has a table seat storing students' names and their corresponding seat ids. +-- +--The column id is continuous increment. +-- +--+---------+---------+ +--| id | student | +--+---------+---------+ +--| 1 | Abbot | +--| 2 | Doris | +--| 3 | Emerson | +--| 4 | Green | +--| 5 | Jeames | +--+---------+---------+ + +--For students with odd id, the new id is (id+1) after switch unless it is the last seat. And for students with even id, the new id is (id-1). +--In order to know how many seats in total, we can use a subquery: +--gotcha is when we do id+1 we shouldn't exceed the row number so when id==count we leave as is + +select (case + when MOD(id,2)!=0 && id!=counts then id+1 + when MOD(id,2)!=0 && id=counts then id + else id-1 + end) as id, student + from + seat, + (select count(*) as counts from seat) as seat_counts + order by id asc \ No newline at end of file diff --git a/src/main/java/SQL/FirstLoginDate.sql b/src/main/java/SQL/FirstLoginDate.sql new file mode 100644 index 0000000..9d78cce --- /dev/null +++ b/src/main/java/SQL/FirstLoginDate.sql @@ -0,0 +1,34 @@ + +--Write an SQL query that reports the first login date for each player. +-- +--The query result format is in the following example: +-- +--Activity table: +--+-----------+-----------+------------+--------------+ +--| player_id | device_id | event_date | games_played | +--+-----------+-----------+------------+--------------+ +--| 1 | 2 | 2016-03-01 | 5 | +--| 1 | 2 | 2016-05-02 | 6 | +--| 2 | 3 | 2017-06-25 | 1 | +--| 3 | 1 | 2016-03-02 | 0 | +--| 3 | 4 | 2018-07-03 | 5 | +--+-----------+-----------+------------+--------------+ +-- +--Result table: +--+-----------+-------------+ +--| player_id | first_login | +--+-----------+-------------+ +--| 1 | 2016-03-01 | +--| 2 | 2017-06-25 | +--| 3 | 2016-03-02 | +--+-----------+-------------+ + + +select player_id, min(event_date) as first_login from Activity group by player_id + + +--the following query is to try and get first loggedin deviceId + +SELECT player_id, device_id FROM Activity +WHERE (player_id, event_date) IN +(SELECT player_id, MIN(event_date) first_date FROM Activity GROUP BY player_id) \ No newline at end of file diff --git a/src/main/java/SQL/ManagerHaving5OrMoreReport.sql b/src/main/java/SQL/ManagerHaving5OrMoreReport.sql new file mode 100644 index 0000000..988a525 --- /dev/null +++ b/src/main/java/SQL/ManagerHaving5OrMoreReport.sql @@ -0,0 +1,22 @@ + +--+------+----------+-----------+----------+ +--|Id |Name |Department |ManagerId | +--+------+----------+-----------+----------+ +--|101 |John |A |null | +--|102 |Dan |A |101 | +--|103 |James |A |101 | +--|104 |Amy |A |101 | +--|105 |Anne |A |101 | +--|106 |Ron |B |101 | +--+------+----------+-----------+----------+ +-- +--Given the Employee table, write a SQL query that finds out managers with at least 5 direct report. For the above table, your SQL query should return: +-- +--+-------+ +--| Name | +--+-------+ +--| John | +--+-------+ + + +select (e2.Name) from Employee e1 inner join Employee e2 on e1.ManagerId=e2.Id group by e1.managerid having count(*) >= 5 diff --git a/src/main/java/SQL/NthHighestSalary.sql b/src/main/java/SQL/NthHighestSalary.sql new file mode 100644 index 0000000..812e4cc --- /dev/null +++ b/src/main/java/SQL/NthHighestSalary.sql @@ -0,0 +1,10 @@ +--N is passed as parameter + +CREATE FUNCTION getNthHighestSalary(N INT) RETURNS INT +BEGIN +DECLARE M INT; #note variable declaration +SET M=N-1; + RETURN ( + select distinct(salary) from Employee order by Salary desc limit M,1 + ); +END \ No newline at end of file diff --git a/src/main/java/SQL/RisingTemperature.sql b/src/main/java/SQL/RisingTemperature.sql new file mode 100644 index 0000000..6e0492d --- /dev/null +++ b/src/main/java/SQL/RisingTemperature.sql @@ -0,0 +1,23 @@ + +--Given a Weather table, write a SQL query to find all dates' Ids with higher temperature compared to its previous (yesterday's) dates. +-- +--+---------+------------------+------------------+ +--| Id(INT) | RecordDate(DATE) | Temperature(INT) | +--+---------+------------------+------------------+ +--| 1 | 2015-01-01 | 10 | +--| 2 | 2015-01-02 | 25 | +--| 3 | 2015-01-03 | 20 | +--| 4 | 2015-01-04 | 30 | +--+---------+------------------+------------------+ + + +--+----+ +--| Id | +--+----+ +--| 2 | +--| 4 | +--+----+ +SELECT wt1.Id +FROM Weather wt1, Weather wt2 +WHERE wt1.Temperature > wt2.Temperature AND + TO_DAYS(wt1.RecordDate)-TO_DAYS(wt2.RecordDate)=1; \ No newline at end of file diff --git a/src/main/java/SQL/SubjectRanks.sql b/src/main/java/SQL/SubjectRanks.sql new file mode 100644 index 0000000..bae55fe --- /dev/null +++ b/src/main/java/SQL/SubjectRanks.sql @@ -0,0 +1,36 @@ + +--+----+-------+ +--| Id | Score | +--+----+-------+ +--| 1 | 3.50 | +--| 2 | 3.65 | +--| 3 | 4.00 | +--| 4 | 3.85 | +--| 5 | 4.00 | +--| 6 | 3.65 | +--+----+-------+ + +--+-------+---------+ +--| score | Rank | +--+-------+---------+ +--| 4.00 | 1 | +--| 4.00 | 1 | +--| 3.85 | 2 | +--| 3.65 | 3 | +--| 3.65 | 3 | +--| 3.50 | 4 | +--+-------+---------+ + + +SELECT + Score, + (SELECT count(distinct Score) FROM Scores WHERE Score >= s.Score) "Rank" -- inner for-loop +FROM Scores s +ORDER BY Score desc + + +SELECT + Score, + (SELECT count(distinct Score) FROM Scores WHERE Score >= s.Score) "Rank" +FROM Scores s +ORDER BY Score desc \ No newline at end of file diff --git a/src/main/java/SQL/TeamSize.sql b/src/main/java/SQL/TeamSize.sql new file mode 100644 index 0000000..37cb0c3 --- /dev/null +++ b/src/main/java/SQL/TeamSize.sql @@ -0,0 +1,31 @@ +--Employee Table: +--+-------------+------------+ +--| employee_id | team_id | +--+-------------+------------+ +--| 1 | 8 | +--| 2 | 8 | +--| 3 | 8 | +--| 4 | 7 | +--| 5 | 9 | +--| 6 | 9 | +--+-------------+------------+ +--Result table: +--+-------------+------------+ +--| employee_id | team_size | +--+-------------+------------+ +--| 1 | 3 | +--| 2 | 3 | +--| 3 | 3 | +--| 4 | 1 | +--| 5 | 2 | +--| 6 | 2 | +--+-------------+------------+ +--Employees with Id 1,2,3 are part of a team with team_id = 8. so team_size is 3 +--Employees with Id 4 is part of a team with team_id = 7. +--Employees with Id 5,6 are part of a team with team_id = 9. + + +# Write your MySQL query statement below + +select employee_id, (select count(*) from Employee tb2 where tb2.team_id=tb1.team_id) as team_size +from Employee tb1 \ No newline at end of file diff --git a/src/main/java/SQL/Top3Salaries.sql b/src/main/java/SQL/Top3Salaries.sql new file mode 100644 index 0000000..a5b0a95 --- /dev/null +++ b/src/main/java/SQL/Top3Salaries.sql @@ -0,0 +1,19 @@ +--to get to 3 salaries from a table +-- select * from Employee where salary >= {value} this value we need to calculate dynamically (some min value in top 3 salaries) +-- so select min(Salary) from (select distinct(salary) from Employee order by Salary DESC limit 3) this will fetch min of top 3 salaries +-- add this to original query +--+----+-------------+-------------+ +--| id | Name | Salary | +--+----+-------------+-------------+ +--| 1 | A | 7000 | +--| 2 | B | 6000 | +--| 3 | C | 6000 | +--| 4 | D | 6000 | +--| 5 | E | 4000 | +--| 6 | F | 3000 | +--| 7 | G | 3000 | +--| 8 | H | 2000 | +--| 9 | I | 1000 | +--+----+-------------+-------------+ + +select * from Employee where salary >= (select min(Salary) from (select distinct(salary) from Employee order by Salary DESC limit 3)) \ No newline at end of file diff --git a/src/main/java/SQL/Top3SalaryDepartmentWise.sql b/src/main/java/SQL/Top3SalaryDepartmentWise.sql new file mode 100644 index 0000000..6f59397 --- /dev/null +++ b/src/main/java/SQL/Top3SalaryDepartmentWise.sql @@ -0,0 +1,22 @@ +-- +--Now, for each row of the outer query: +--OuterDepartmentId, OuterEmployeeSalary is available to the inner query. +--The inner query will fetch all the salaries that are greater then OuterEmployeeSalary for department matching OuterDepartmentId +--and return a count of such distinct salaries +-- +--This count can be 0,1 or 2 +-- +--if 0 -> that means there are no salaries greater then the OuterDepartmentSalary in that department. Hence, it is the greatest salary for that department. And outer query will include that OuterDepartmentId, OuterEmployeeSalary in the output. +-- +--if 1 -> there is one salary bigger then OuterEmployeeSalary (it is the second largest salary) +-- +--similarly for count 2, there are two larger salaries. + +select d.Name Department, e1.Name Employee, e1.Salary +from Employee e1 +join Department d +on e1.DepartmentId = d.Id +where 3>(select count(distinct(e2.Salary)) + from Employee e2 + where e2.Salary > e1.Salary + and e1.DepartmentId = e2.DepartmentId) -- the inner query will act as inner for loop which get e.salary from outside O(n^2) \ No newline at end of file diff --git a/src/main/java/SQL/TopKthSalary.sql b/src/main/java/SQL/TopKthSalary.sql new file mode 100644 index 0000000..0f3fe91 --- /dev/null +++ b/src/main/java/SQL/TopKthSalary.sql @@ -0,0 +1,5 @@ +SELECT IFNULL((SELECT DISTINCT Salary + FROM Employee + ORDER BY Salary DESC + LIMIT 1,N-1),NULL) AS NthHighestSalary + diff --git a/src/main/java/SQL/WinningCandidate.sql b/src/main/java/SQL/WinningCandidate.sql new file mode 100644 index 0000000..4313ba7 --- /dev/null +++ b/src/main/java/SQL/WinningCandidate.sql @@ -0,0 +1,10 @@ + + +SELECT Name +FROM Candidate c +INNER JOIN (SELECT CandidateId + FROM Vote + GROUP BY CandidateId + ORDER BY COUNT(CandidateId) DESC + LIMIT 0,1) v +ON c.id = v.CandidateId \ No newline at end of file diff --git a/src/main/java/backtracking/DistributeStonesInGrid.java b/src/main/java/backtracking/DistributeStonesInGrid.java new file mode 100644 index 0000000..05c4516 --- /dev/null +++ b/src/main/java/backtracking/DistributeStonesInGrid.java @@ -0,0 +1,70 @@ +package backtracking; + +/** + * https://leetcode.com/problems/minimum-moves-to-spread-stones-over-grid/ + * + * Brute force solution + * DFS + */ +public class DistributeStonesInGrid { + int ret = Integer.MAX_VALUE; + public int minimumMoves(int[][] grid) { + dfs(grid, 0); + return ret; + } + + /** + * Iterate through all possible pairs of source (cell value > 1) and destination cells (cell value == 0) + * in the grid to check if it's possible to move a stone from the source cell to the destination cell. + * + * Check if the Destination cell is empty (contains 0 stones) and if the Source cell contains more than 1 stone. + * Decrease the number of stones in the Source by 1 and increase stones in the Destination cell by 1. + * Calculate the Manhattan distance between the Source and the Destination. + * Backtracking step: After exploration, restore the original grid configuration, i.e, reverse of step 2. + * The count variable keeps track of the minimum number of moves needed to achieve the desired configuration. + */ + private void dfs(int[][] grid, int move) { + int zx = -1; + int zy = -1; + + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + if (grid[i][j] == 0) { + zx = i; + zy = j; + break; + + } + } + } + + // this is the base case, because of the constraint + if (zx == -1 && zy == -1) { + ret = Math.min(ret, move); + return; + } + + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + if (grid[i][j] > 1) { + + grid[i][j] -= 1; + grid[zx][zy] += 1; + + int dx = Math.abs(i - zx); + int dy = Math.abs(j - zy); + + dfs(grid, move + dx + dy); + + // the reason we reset is to try all combinations + // if the result changes, it means we have found a better solution + // line 28 will take care of updating the result + grid[i][j] += 1; + grid[zx][zy] -= 1; + + } + } + } + + } +} diff --git a/src/main/java/binarysearch/AggressiveCows.java b/src/main/java/binarysearch/AggressiveCows.java new file mode 100644 index 0000000..f3ed02f --- /dev/null +++ b/src/main/java/binarysearch/AggressiveCows.java @@ -0,0 +1,67 @@ +package binarysearch; + +// https://takeuforward.org/data-structure/aggressive-cows-detailed-solution/ + +import java.util.Arrays; + +/** + * Example 1: + * Input Format: + * N = 6, k = 4, arr[] = {0,3,4,7,10,9} + * Result: + * 3 + * Explanation: + * The maximum possible minimum distance between any two cows will be 3 when 4 cows are placed at positions {0, 3, 7, 10}. + * Here the distances between cows are 3, 4, and 3 respectively. + * We cannot make the minimum distance greater than 3 in any ways. + */ +public class AggressiveCows { + + public int aggressiveCows(int[] stalls, int cows){ + //To arrange the cows in a consecutive manner while ensuring a certain distance between them, + // the initial step is to sort the stalls based on their positions. + // In a sorted array, the minimum distance will always be obtained from any two consecutive cows + Arrays.sort(stalls); + //The minimum possible distance between two cows is 1 as the minimum distance between 2 consecutive stalls is 1. + //The maximum possible distance between two cows is = max(stalls[])-min(stalls[]). This case occurs when we place 2 cows at two ends of the sorted stalls array. + int right = stalls[stalls.length-1] - stalls[0]; + int left=1; + + while(left= distance){ + count++; + lastPosition = stalls[j]; + } + } + + return count >= cows; + } +} diff --git a/src/main/java/binarysearch/AssignBooks.java b/src/main/java/binarysearch/AssignBooks.java new file mode 100644 index 0000000..643b4d4 --- /dev/null +++ b/src/main/java/binarysearch/AssignBooks.java @@ -0,0 +1,45 @@ +package binarysearch; + +import java.util.Arrays; + +// https://leetcode.com/problems/split-array-largest-sum/ +//https://takeuforward.org/data-structure/allocate-minimum-number-of-pages/ +public class AssignBooks { + + private static int getPieces(int[] nums, int mid) { + int sum = 0; // Current sum of the subarray + int pieces = 1; // At least one subarray is needed + + for (int num : nums) { + if (sum + num > mid) { // If adding num to current sum exceeds mid + sum = num; // Start a new subarray + pieces++; // Increase the count of subarrays + } else { + sum += num; // Continue adding to the current subarray + } + } + return pieces; + } + + public int splitArray(int[] nums, int k) { + // Initial boundary values for binary search + int start = Arrays.stream(nums).max().orElse(0); // The minimum possible largest sum is the max element in the array + int end = Arrays.stream(nums).sum(); // The maximum possible largest sum is the sum of all elements in the array + + // Binary search for the minimum possible largest sum + while (start < end) { + int mid = start + (end - start) / 2; // Middle value between start and end + int pieces = getPieces(nums, mid); + + // If more subarrays are needed than allowed, increase the lower bound + if (pieces > k) { + start = mid + 1; // Increase the lower bound since mid is too small + } else { + end = mid; // Decrease the upper bound since mid could be the answer + } + } + + // When start == end, we've found the minimum possible largest sum + return start; // or return end; since start == end + } +} diff --git a/src/main/java/binarysearch/BinarySearchTemplate.java b/src/main/java/binarysearch/BinarySearchTemplate.java new file mode 100644 index 0000000..98f37bd --- /dev/null +++ b/src/main/java/binarysearch/BinarySearchTemplate.java @@ -0,0 +1,48 @@ +package binarysearch; + +public class BinarySearchTemplate { + + /** + * while (left < right): + * This condition is typically used when you want to find the insertion point or a specific boundary in a sorted array. + * It's often used when you're not necessarily looking for an exact match, + * but rather a position where an element should be inserted to maintain the sorted order. + * while (left <= right): + * This condition is used when you're searching for a specific element in the array + * and want to return its index if found. + * It allows the search to continue until the pointers cross each other, ensuring that every element is checked. + + * The key differences are: + * + * Termination condition: + * + * left < right will terminate when left == right + * left <= right will terminate when left > right + * + * Final state: + * + * With left < right, left will be the insertion point or boundary you're looking for + * With left <= right, you need to check if the element was found after the loop ends + * + * Use case: + * + * left < right is often used for problems like finding the first/last occurrence of an element, or finding an insertion point + * left <= right is typically used when you're searching for a specific element and want to return its index + * + */ + +// public T template(int n) { +// +// int left= min_val; +// int right= max_val; +// +// while(left... + * + * @param arr + * @return + */ + public boolean validMountainArray(int[] arr) { + int left = 0; + int right = arr.length; + int peak = -1; + while (left <= right) { + int mid = left + (right - left) / 2; + System.out.println("mid: " + mid); + if (mid > 0 && arr[mid] > arr[mid - 1] && mid < arr.length - 1 && arr[mid + 1] < arr[mid]) { + peak = mid; + break; + } + + if (mid < arr.length - 1 && arr[mid] < arr[mid + 1]) { + left = mid + 1; + } else { + right = mid - 1; + } + } + + if (peak == -1) + return false; + for (int i = 0; i < peak; i++) { + if (arr[i] >= arr[i + 1]) + return false; + } + // peak to n - 1 + for (int i = peak; i < arr.length - 1; i++) { + if (arr[i] <= arr[i + 1]) + return false; + } + return true; + } + + //https://leetcode.com/problems/find-peak-element + public int findPeakElement(int[] nums) { + //if(nums.length) + int left = 0; + int right = nums.length - 1; + + while (left <= right) { + int mid = (left + right) / 2; + if ((mid == 0 || nums[mid - 1] < nums[mid]) && (mid == nums.length - 1 || nums[mid + 1] < nums[mid])) { + return mid; + } else if (mid == 0 || nums[mid - 1] < nums[mid] && nums[mid + 1] > nums[mid]) { + left = mid + 1; + } else { + right = mid - 1; + } + } + return -1; + } + + //https://leetcode.com/problems/peak-index-in-a-mountain-array/description/ + public int peakIndexInMountainArray(int[] arr) { + + int left = 0; + int right = arr.length - 1; + + while (left <= right) { + int mid = left + (right - left) / 2; + if (arr[mid] > arr[mid - 1] && (arr[mid] > arr[mid + 1] || mid == arr.length - 1)) { + return mid; + } + if (arr[mid] > arr[mid - 1]) { + left = mid + 1; + } else { + right = mid; + } + } + + return -1; + } + + //https://leetcode.com/problems/find-in-mountain-array/ + public int findInMountainArray(int target, MountainArray mountainArr) { + int left = 0; + int n = mountainArr.length(); + int right = n - 1; + int peak = 0; + int mid = 0; + while (left < right) { + mid = (left + right) / 2; + if (mountainArr.get(mid) < mountainArr.get(mid + 1)) { + left = mid + 1; + + } else { + right = mid; + } + } + peak = left; + left = 0; + right = peak; + + while (left <= right) { + mid = (left + right) / 2; + if (mountainArr.get(mid) < target) { + left = mid + 1; + } else if (mountainArr.get(mid) > target) { + right = mid - 1; + } else { + return mid; + } + } + + left = peak; + right = n - 1; + + while (left <= right) { + mid = (left + right) / 2; + if (mountainArr.get(mid) > target) { + left = mid + 1; + } else if (mountainArr.get(mid) < target) { + right = mid - 1; + } else { + return mid; + } + } + + return -1; + + } + + //https://leetcode.com/problems/longest-mountain-in-array + // O(N) solution is fine for this constraint + public int longestMountain(int[] A) { + if (A == null || A.length < 3) + return 0; + int ans = 0; + for (int i = 1; i < A.length - 1; i++) { + if (A[i] > A[i - 1] && A[i] > A[i + 1]) { // i is a peak + int left = i - 1; // find leftmost of the peak + while (left > 0 && A[left - 1] < A[left]) + left--; + + int right = i + 1; // find rightmost of the peak + while (right < A.length - 1 && A[right + 1] < A[right]) + right++; + + ans = Math.max(ans, right - left + 1); // get the width using left and rightmost + + } + } + return ans; + } + + //https://leetcode.com/problems/find-peak-element-ii/ + // https://youtu.be/nGGp5XBzC4g + public int[] findPeakGrid(int[][] mat) { + + int left = 0; + int right = mat[0].length; + int m = mat.length; + //In this scenario, we'll employ binary search within the columns of the matrix. + // Since the peak element is guaranteed to exist in one of the columns, + // our search range will span from 0 to M, where M represents the total number of columns. + int n = mat[0].length; + while (left <= right) { + int mid = (left + right) / 2; + //Find the largest element in the ‘mid’ column: We will use the function findMaxIndex() + // to find the largest element of the mid-th column and the function will return the row number + int maxRow = getMaxElementRow(mat, mid); + + int prev = mid - 1 >= 0 ? mat[maxRow][mid - 1] : -1; + int next = mid + 1 < n ? mat[maxRow][mid + 1] : -1; + + //If matrix[maxRowIndex][mid] > matrix[maxRowIndex][mid-1] && matrix[maxRowIndex][mid] > matrix[maxRowIndex][mid+1]: + // This means matrix[maxRowIndex][mid] is the peak element. So, we will return its location + if (mat[maxRow][mid] > prev && mat[maxRow][mid] > next) { + return new int[]{maxRow, mid}; + } + //For example, if matrix[i][j-1] is greater than the chosen largest element matrix[i][j], + // we can conclude that matrix[i][j-1] is also greater than all the elements of the j-th column. + // This is because matrix[i][j] is the largest element of j-th column. + // Thus matrix[i][j-1] is now more likely to be the peak element. + // The logic is also applied to matrix[i][j+1]. This is how the elimination is working. + if (mat[maxRow][mid] < next) { + + left = mid + 1; + } else { + //If matrix[maxRowIndex][mid] < matrix[maxRowIndex][mid-1]: As we are in the right half of the peak element, + // we have to eliminate this right half (i.e. high = mid-1). Because our peak element appears somewhere on the left side. + right = mid - 1; + } + } + + return new int[]{-1, -1}; + + } + + public int getMaxElementRow(int[][] mat, int col) { + + int rowNum = -1; + int maxElem = Integer.MIN_VALUE; + for (int i = 0; i < mat.length; i++) { + //Although every element of the selected column might be a peak element, + // the largest number has always the highest possibility of being one. + // That is why, to reduce the checking operation, we are selecting the largest number. + if (mat[i][col] > maxElem) { + maxElem = mat[i][col]; + rowNum = i; + } + } + + return rowNum; + } +} \ No newline at end of file diff --git a/src/main/java/binarysearch/BoatsToSave.java b/src/main/java/binarysearch/BoatsToSave.java new file mode 100644 index 0000000..fa5a759 --- /dev/null +++ b/src/main/java/binarysearch/BoatsToSave.java @@ -0,0 +1,36 @@ +package binarysearch; + +import java.util.Arrays; + +/** + * The i-th person has weight people[i], + * and each boat can carry a maximum weight of limit. + * Each boat carries at most 2 people at the same time, + * provided the sum of the weight of those people is at most limit. + * Return the minimum number of boats to carry every given person. + * (It is guaranteed each person can be carried by a boat.) + */ +class BoatsToSave { + public int numRescueBoats(int[] people, int limit) { + if (people.length == 0 || limit == 0) return 0; + + Arrays.sort(people); + + int i = 0; + int j = people.length - 1; + int res = 0; + while (i <= j) { + if (people[i] + people[j] <= limit) { + res++; + i++; + j--; + } else { + res++; + j--;// neglecting people with more weight + } + + } + + return res; + } +} \ No newline at end of file diff --git a/src/main/java/binarysearch/FindMinimumInRotatedArray.java b/src/main/java/binarysearch/FindMinimumInRotatedArray.java new file mode 100644 index 0000000..eb8f598 --- /dev/null +++ b/src/main/java/binarysearch/FindMinimumInRotatedArray.java @@ -0,0 +1,59 @@ +package binarysearch; + +/** + * https://leetcode.com/problems/find-minimum-in-rotated-sorted-array/ + */ +public class FindMinimumInRotatedArray { + + public static int findMin(int[] nums) { + if (nums.length == 0) { + return -1; + } + if (nums.length == 1) { + return nums[0]; + } + int start = 0; + int end = nums.length - 1; + while (start < end) { + int mid = ((end - start) / 2) + start; + + if (nums[start] <= nums[end]) { + return nums[start]; + } + // mid is compared against end, if mid is high, then the rotated part is right of mid + if (nums[mid] < nums[end]) { + end = mid; + } else { + start = mid + 1; + } + } + return -1; + } + + public int findMinWithDuplicate(int[] nums) { + if (nums.length == 1) return nums[0]; + int start = 0; + int end = nums.length - 1; + while (start < end) { + + if (nums[start] == nums[end]) { + start++; // conservative approach that ensures we don't accidentally skip over the minimum element. + } else { + int mid = start + (end - start) / 2; + if (nums[mid] > nums[end]) { + start = mid + 1; + } else { + end = mid; + } + } + + } + + return nums[start]; + } + + public static void main(String[] args) { + int[] arr = {7, 1, 2, 3, 4, 5, 6}; + findMin(arr); + } +} \ No newline at end of file diff --git a/src/main/java/binarysearch/FindSmallestDivisor.java b/src/main/java/binarysearch/FindSmallestDivisor.java new file mode 100644 index 0000000..df2cf2e --- /dev/null +++ b/src/main/java/binarysearch/FindSmallestDivisor.java @@ -0,0 +1,32 @@ +package binarysearch; + +import java.util.Arrays; + +/** + * https://leetcode.com/problems/find-the-smallest-divisor-given-a-threshold/ + */ +public class FindSmallestDivisor { + public int smallestDivisor(int[] nums, int threshold) { + int left = 1; + int right = Arrays.stream(nums).max().orElse(0); + + while (left < right) { + int mid = left + (right - left) / 2; + if (isFeasible(nums, mid, threshold)) { + right = mid; + } else { + left = mid + 1; + } + } + + return left; + } + + public boolean isFeasible(int[] nums, int mid, int threshold) { + int total = 0; + for (int num : nums) { + total += (int) Math.ceil((double) num / mid); + } + return total <= threshold; + } +} diff --git a/src/main/java/binarysearch/FirstAndLastOccurence.java b/src/main/java/binarysearch/FirstAndLastOccurence.java new file mode 100644 index 0000000..1529edf --- /dev/null +++ b/src/main/java/binarysearch/FirstAndLastOccurence.java @@ -0,0 +1,81 @@ +package binarysearch; + +/** + * https://leetcode.com/problems/find-first-and-last-position-of-element-in-sorted-array/submissions/ + */ +public class FirstAndLastOccurence { + + public int[] searchRange(int[] nums, int target) { + int[] result = new int[]{-1, -1}; + if (nums.length == 0) return result; + + int first = binarySearch(nums, target); + + if (first == nums.length || nums[first] != target) return result; + + result[0] = first; + int last = binarySearch(nums, target + 1); + + result[1] = nums[last] == target ? last : last - 1; + + return result; + + } + + public int binarySearch(int[] nums, int target) { + int left = 0; + int right = nums.length - 1; + + while (left < right) { + int mid = left + (right - left) / 2; + + if (nums[mid] >= target) { + right = mid; + } else { + left = mid + 1; + + } + } + + return left; + } + public int[] searchRangeII(int[] nums, int target) { + int low = 0; + int high = nums.length - 1; + int[] ans = {-1 , -1}; + + if(nums.length < 1){ + return ans; + } + while(low <= high){ //Binary search for 1st occurrence + int mid = (low + high)/2; + if(target == nums[mid]){ + ans[0] = mid; + high = mid -1; //searching on the left half + } + else if(target > nums[mid]){ + low = mid + 1; + } + else{ + high = mid - 1; + } + } + + low = 0; //Binary search for 2nd occurrence + high = nums.length - 1; + while(low <= high){ + int mid = (low + high)/2; + if(target == nums[mid]){ + ans[1] = mid; + low = mid + 1; //searching on the right half + } + else if(target > nums[mid]){ + low = mid + 1; + } + else{ + high = mid - 1; + } + } + return ans; + } +} \ No newline at end of file diff --git a/src/main/java/binarysearch/FirstBadVersion.java b/src/main/java/binarysearch/FirstBadVersion.java new file mode 100644 index 0000000..a2da91c --- /dev/null +++ b/src/main/java/binarysearch/FirstBadVersion.java @@ -0,0 +1,25 @@ +package binarysearch; + +/** + * https://leetcode.com/problems/first-bad-version/ + */ +public class FirstBadVersion { + + public int firstBadVersion(int n) { + if(n==0) return 0; + int left=1; + int right=n; + + while(left... + *

+ * one of the trick involved in computing rage queries over prefix sum is use the given query index to calculate the prefix-sum for ex + *

+ * if queries are from index (1 , 5) value is 4 and from index (3,7) value is 6, then the prefix array would look like + * [0,5,0,6,0,0,-5,0,0,-6,0,0] => when prefix sum is calculated then the array would get transformed to [0,5,5,11,11,11,6,6,6,0,0,0] + * the advantage is update query run time is O(1) as we are touching the specific index at that point, when get query comes we have to traverse the array and it's complexity is O(N) + *

+ * we can improve the above array storage space by removing 0 entries with the help of sparse array(a simple hashmap to store index and value at that place) + */ +public class FlowerBloomSparseArray { + // Blooming flowers = started flowers - ended flowers + // Collect start bloom time point array, then sort it. + //Collect end bloom time point array, then sort it. + // For each time point t in persons: + // + //Binary search the upper bound of t in start, then we find the started flowers. + //Binary search the lower bound of t in end, then we find the started flowers. + //Blooming flowers = started flowers - ended flowers + public static int[] fullBloomFlowersBinarySearch(int[][] flowers, int[] people) { + List starts = new ArrayList<>(); + List ends = new ArrayList<>(); + + + for (int[] flower: flowers) { + starts.add(flower[0]); + ends.add(flower[1] + 1); // Note that a flower = [start, end] stops blooming at end + 1, not end. + } + + Collections.sort(starts); + Collections.sort(ends); + int[] ans = new int[people.length]; + + for (int index = 0; index < people.length; index++) { + int person = people[index]; + int i = binarySearch(starts, person); + int j = binarySearch(ends, person); + ans[index] = i - j; + } + + return ans; + } + + public static int binarySearch(List arr, int target) { + int left = 0; + int right = arr.size(); + while (left < right) { + int mid = (left + right) / 2; + if (target < arr.get(mid)) { + right = mid; + } else { + left = mid + 1; + } + } + + return left; + } + + + public static void main(String[] args) { + fullBloomFlowersBinarySearch(new int[][]{{1, 6}, {3, 7}, {9, 12}, {4, 13}}, new int[]{2, 3, 7, 11}); + } + + public int[] fullBloomFlowers(int[][] flowers, int[] persons) { + int BLOOM = 0, WILT = 1, PERSON = 2; + int[] arr = new int[persons.length]; + PriorityQueue pq = new PriorityQueue<>((a,b) -> a[0] == b[0] ? a[1] - b[1] : a[0] - b[0]); + for (int[] flower : flowers) { + pq.offer(new int[]{flower[0], BLOOM}); + pq.offer(new int[]{flower[1], WILT}); + } + for(int i = 0; i < persons.length; i++){ + pq.offer(new int[]{persons[i],PERSON,i}); + } + int count = 0; + while(!pq.isEmpty()){ + int[] temp = pq.poll(); + + if(temp[1] == BLOOM) count++; + else if(temp[1] == WILT) count--; + else{ + arr[temp[2]] = count; + } + } + return arr; + } +} diff --git a/src/main/java/binarysearch/KClosestElements.java b/src/main/java/binarysearch/KClosestElements.java new file mode 100644 index 0000000..3326822 --- /dev/null +++ b/src/main/java/binarysearch/KClosestElements.java @@ -0,0 +1,113 @@ +package binarysearch; + +import java.util.ArrayList; +import java.util.List; + +/** + * https://leetcode.com/problems/find-k-closest-elements/ + * Revise + */ +public class KClosestElements { + /** + * Now let's see how x - arr[mid] > arr[mid + k] - x works: + * Say this array is sorted from small to large: [x1, arr[mid], x2, arr[mid + k], x3] + * + * Case x1: + * x1 is smaller than both arr[mid] and arr[mid + k]. We should recurse into the left. + * x - arr[mid] is negative and arr[mid + k] - x is positive. + * The condition is false and we recurse to left. + * + * Case x2: + * x2 is in between arr[mid] and arr[mid + k]. We should recurse to the side that is closer to x. + * x - arr[mid] and arr[mid + k] - x are both positive. + * The comparison works the same as using abs(); we recursive to the closer side. + * (When in the case of arr[mid] == arr[mid + k], the condition is false and we recurse to the left.) + * + * Case x3: + * x3 is larger than both arr[mid] and arr[mid + k]. We should recurse into the right. + * x - arr[mid] is positive and arr[mid + k] - x is negative. + * The condition is true and we recurse to right. + * Now the previously failed corner case arr[mid] == arr[mid + k] < x3 doesn't cause a problem anymore + * because the positivity of the numbers we're comparing is still the same. + */ + public List findClosestElements(int[] arr, int k, int x) { + // Initialize binary search bounds + // we need to find the left most element which will be the start of the result set + int left = 0; + // the farthest it can go is array-k + int right = arr.length - k; // else the mid+k will go out of bound + + // Binary search against the criteria described + while (left < right) { + int mid = (left + right) / 2; + // If the element at arr[mid] is closer to x than arr[mid + k], + // then that means arr[mid + k], as well as every element to the right of it can never be in the answer. + // This means we should move our right pointer to avoid considering them + if (x - arr[mid] > arr[mid + k] - x) { + left = mid + 1; + } else { + right = mid; + } + } + + // Create output in correct format + List result = new ArrayList<>(); + for (int i = left; i < left + k; i++) { + result.add(arr[i]); + } + + return result; + } + + public List findClosestElementsIntuitive(int[] arr, int k, int x) { + int left = 0, right = arr.length - 1, mid; + List res = new ArrayList<>(); + + while (left < right) { + mid = (left + right) / 2; + if (arr[mid] >= x) { + right = mid; + } else { + left = mid + 1; + } + } + int leftPointer = left - 1; + int rightPointer = left; + + while (rightPointer - leftPointer <= k) { + if (leftPointer < 0) { + rightPointer += 1; + continue; + } + if (rightPointer == arr.length || Math.abs(arr[leftPointer] - x) <= Math.abs(arr[rightPointer] - x)) { + leftPointer -= 1; + } else { + rightPointer += 1; + } + } + + for (int i = leftPointer + 1; i < rightPointer; i++) { + res.add(arr[i]); + } + + return res; + } + + public List findClosestElementsTwoPointers(int[] arr, int k, int x) { + int lo = 0; + int hi = arr.length - 1; + while (hi - lo >= k) { + if (Math.abs(arr[lo] - x) > Math.abs(arr[hi] - x)) { + lo++; + } else { + hi--; + } + } + List result = new ArrayList<>(k); + for (int i = lo; i <= hi; i++) { + result.add(arr[i]); + } + return result; + } + +} \ No newline at end of file diff --git a/src/main/java/binarysearch/KokoEatingBananas.java b/src/main/java/binarysearch/KokoEatingBananas.java new file mode 100644 index 0000000..9066645 --- /dev/null +++ b/src/main/java/binarysearch/KokoEatingBananas.java @@ -0,0 +1,41 @@ +package binarysearch; + +import java.util.Arrays; + +/** + * https://leetcode.com/problems/koko-eating-bananas/ + */ +public class KokoEatingBananas { + + public int minEatingSpeed(int[] piles, int h) { + + int left = 1; + // if you feel this would take O(N) then take the max value + // given as part of problem constraint + + int right = Arrays.stream(piles).max().orElse(0); + + while (left < right) { + int mid = left + (right - left) / 2; + + if (isFeasible(piles, mid, h)) { + right = mid; + } else { + left = mid + 1; + } + } + + return left; + + } + + public boolean isFeasible(int[] piles, int mid, int h) { + int time = 0; + + for (int p : piles) { + time += (int) Math.ceil((double) (p) / (double) (mid)); + } + + return time <= h; + } +} diff --git a/src/main/java/binarysearch/KthMissingPositive.java b/src/main/java/binarysearch/KthMissingPositive.java new file mode 100644 index 0000000..5359c0c --- /dev/null +++ b/src/main/java/binarysearch/KthMissingPositive.java @@ -0,0 +1,24 @@ +package binarysearch; + +public class KthMissingPositive { + + public int findKthPositive(int[] arr, int k) { + int left = 0; + int right = arr.length; //Initialize right to arr.length instead of arr.length - 1. + // This allows the search to consider the position just after the array's end. + //[1,2,3,4], k = 2, the answer is 6, not 5. + + while (left < right) { + int mid = left + (right - left) / 2; + + int diff = arr[mid] - (mid + 1); + if (diff < k) { + left = mid + 1; + } else { + right = mid; + } + } + + return left + k; + } +} diff --git a/src/main/java/binarysearch/KthSmallestFromTwoSortedArrays.java b/src/main/java/binarysearch/KthSmallestFromTwoSortedArrays.java new file mode 100644 index 0000000..0d76e4c --- /dev/null +++ b/src/main/java/binarysearch/KthSmallestFromTwoSortedArrays.java @@ -0,0 +1,49 @@ +package binarysearch; + +/** + * this is same as median for 2 sorted arrays, with little bit modification + * + */ +class KthSmallestFromTwoSortedArrays { + + + public double findKthSmallestFromSortedArrays(int[] input1, int[] input2, int k) { + //if input1 length is greater than switch them so that input1 is smaller than input2. + // the reason is to do binary search on smaller array to reduce time complexity + if (input1.length > input2.length) { + return findKthSmallestFromSortedArrays(input2, input1,k); + } + int x = input1.length; + int y = input2.length; + int n = x + y; + + int low = 0; + int high = x; + while (low <= high) { + int mid1 = (low + high) / 2; + int mid2 = k - mid1; + + int left1 = (mid1 == 0) ? Integer.MIN_VALUE : input1[mid1 - 1]; + int right1 = (mid1 == x) ? Integer.MAX_VALUE : input1[mid1]; + int left2 = (mid2 == 0) ? Integer.MIN_VALUE : input2[mid2 - 1]; + int right2 = (mid2 == y) ? Integer.MAX_VALUE : input2[mid2]; + + if (left1 <= right2 && left2 <= right1) { + return Math.max(left1, left2); + } else if (left1 > right2) { + //If l1 > r2: This implies that we have considered more elements from arr1[] than necessary. + // So, we have to take less elements from arr1[] and more from arr2[]. + // In such a scenario, we should try smaller values of x. + // To achieve this, we will eliminate the right half (high = mid-1). + high = mid1 - 1; + } + //This implies that we have considered more elements from arr2[] than necessary. + // So, we have to take less elements from arr2[] and more from arr1[]. + // In such a scenario, we should try bigger values of x. + // To achieve this, we will eliminate the left half (low = mid+1). + else low = mid1 + 1; + + } + return -1.0; + } +} diff --git a/src/main/java/binarysearch/KthSmallestInMultiplicationTable.java b/src/main/java/binarysearch/KthSmallestInMultiplicationTable.java new file mode 100644 index 0000000..b07b03e --- /dev/null +++ b/src/main/java/binarysearch/KthSmallestInMultiplicationTable.java @@ -0,0 +1,72 @@ +package binarysearch; + +/** + * https://leetcode.com/problems/kth-smallest-number-in-multiplication-table/ + * + * For Kth-Smallest problems like this, what comes to our mind first is Heap. + * Usually we can maintain a Min-Heap and just pop the top of the Heap for k times. + * However, that doesn't work out in this problem. + * We don't have every single number in the entire Multiplication Table, instead, we only have the height and the length of the table. + * If we are to apply Heap method, we need to explicitly calculate these m * n values and save them to a heap. + * The time complexity and space complexity of this process are both O(mn), which is quite inefficient. + * This is when binary search comes in. Remember we say that designing condition function is the most difficult part? + * In order to find the k-th smallest value in the table, we can design an enough function, + * given an input num, determine whether there're at least k values less than or equal to num. + * The minimal num satisfying enough function is the answer we're looking for. + * Recall that the key to binary search is discovering monotonicity. + * In this problem, if num satisfies enough, then of course any value larger than num can satisfy. + * This monotonicity is the fundament of our binary search algorithm. + */ +public class KthSmallestInMultiplicationTable { + + public int findKthNumber(int m, int n, int k) { + int left=1; + int right= m*n; + + while(left=k if yes adjust the right side + int temp= Math.min(mid/i,n); + System.out.println("I :: "+i+" N:: "+n+" MID/N ::"+temp); + count+=temp; + } + System.out.println("isFeasible end:: "+count); + System.out.println(); + return count>=k; + } + + public static void main(String[] args) { + System.out.println(new KthSmallestInMultiplicationTable().findKthNumber(4,4,6)); + } +} diff --git a/src/main/java/binarysearch/MagneticForceBetweenTwoBalls.java b/src/main/java/binarysearch/MagneticForceBetweenTwoBalls.java new file mode 100644 index 0000000..c31d1c3 --- /dev/null +++ b/src/main/java/binarysearch/MagneticForceBetweenTwoBalls.java @@ -0,0 +1,52 @@ +package binarysearch; + +import java.util.Arrays; + +/** + * https://leetcode.com/problems/magnetic-force-between-two-balls/ + *

+ * same as aggressive cows + */ +public class MagneticForceBetweenTwoBalls { + + + public static void main(String[] args) { + new MagneticForceBetweenTwoBalls().maxDistance(new int[]{1, 2, 3, 4, 7}, 3); + } + + /*minimum magnetic force between any two balls is maximum means + ex: 1 2 3 4 7 + . . . min magnetic force is 1 min is 1 + . . . min magnetic force is 2 min is 1 + . . . min magnetic force is 3 * + we should maximise the minimum magnetic force + minimum magnetic must come 1 from all cases and if we have maximise it then it will come as 3 3 6 + */ + public int maxDistance(int[] position, int m) { + int n = position.length; + Arrays.sort(position); + int left = 1, right = position[n - 1] - position[0]; + while (left <= right) { + int mid = left + (right - left) / 2; + if (check(position, mid, m)) + left = mid + 1; + else + right = mid - 1; + } + //When the binary search ends, right will be the largest distance that satisfies the condition, + // and left will be one more than this value. + return right; + } + + private boolean check(int[] arr, int mid, int m) { + int count = 1, last = arr[0]; + for (int i = 1; i < arr.length; i++) { + if (arr[i] - last >= mid) { + count++; + last = arr[i]; + if (count >= m) return true; + } + } + return false; + } +} diff --git a/src/main/java/binarysearch/MaxSoldiers.java b/src/main/java/binarysearch/MaxSoldiers.java new file mode 100644 index 0000000..1e1d0c9 --- /dev/null +++ b/src/main/java/binarysearch/MaxSoldiers.java @@ -0,0 +1,53 @@ +package binarysearch; + +import java.util.PriorityQueue; + +/** + * https://leetcode.com/problems/the-k-weakest-rows-in-a-matrix/ + */ +public class MaxSoldiers { + static class Pair { + T row; + S soldiers; + + Pair(T row, S soldiers) { + this.row = row; + this.soldiers = soldiers; + } + } + + public int[] kWeakestRows(int[][] mat, int k) { + int[] result = new int[k]; + PriorityQueue> queue = new PriorityQueue<>( + (a, b) -> a.soldiers.equals(b.soldiers) ? Integer.compare(a.row, b.row) + : Integer.compare(a.soldiers, b.soldiers)); + int i = 0; + for (int[] rows : mat) { + int temp = binarySearchUtil(rows, 0, rows.length); + queue.offer(new Pair<>(i, temp)); + i++; + } + int ind = 0; + while (ind < k) { + result[ind++] = queue.poll().row; + } + return result; + + } + + public int binarySearchUtil(int[] row, int start, int end) { + int lo = 0; + int hi = row.length; + + while (lo < hi) { + int mid = lo + (hi - lo) / 2; + + if (row[mid] == 1) + lo = mid + 1; + else + hi = mid; + } + + return lo; + } +} diff --git a/src/main/java/binarysearch/MaximumDistanceBetweenGasStation.java b/src/main/java/binarysearch/MaximumDistanceBetweenGasStation.java new file mode 100644 index 0000000..8ae0098 --- /dev/null +++ b/src/main/java/binarysearch/MaximumDistanceBetweenGasStation.java @@ -0,0 +1,128 @@ +package binarysearch; + +// https://leetcode.com/problems/minimize-max-distance-to-gas-station +// https://takeuforward.org/arrays/minimise-maximum-distance-between-gas-stations/ +// extreme tricky hard problem + +/** + * You are given a sorted array ‘arr’ of length ‘n’, which contains positive integer positions of ‘n’ gas stations on the X-axis. + * You are also given an integer ‘k’. You have to place 'k' new gas stations on the X-axis. + * You can place them anywhere on the non-negative side of the X-axis, even on non-integer positions. + * Let 'dist' be the maximum value of the distance between adjacent gas stations after adding k new gas stations. + * Find the minimum value of ‘dist’. + *

+ * Input Format: + * N = 5, arr[] = {1,2,3,4,5}, k = 4 + * Result: + * 0.5 + * Explanation: + * One of the possible ways to place 4 gas stations is {1,1.5,2,2.5,3,3.5,4,4.5,5}. + * Thus the maximum difference between adjacent gas stations is 0.5. + * Hence, the value of ‘dist’ is 0.5. It can be shown that there is no possible way to add 4 gas stations in such a way that the value of ‘dist’ is lower than this. + */ +public class MaximumDistanceBetweenGasStation { + + public double minimiseMaxDistanceBruteForce(int[] stations, int k) { + int n = stations.length; + int[] howManyLeft = new int[n - 1]; + + for (int gasStations = 1; gasStations <= k; gasStations++) { + double maxSelection = -1; + int maxIndex = -1; + + for (int i = 0; i < n - 1; i++) { + double diff = (stations[i + 1] - stations[i]); + double selectionLength = diff / (howManyLeft[i] + 1); + if (selectionLength > maxSelection) { + maxSelection = selectionLength; + maxIndex = i; + } + } + howManyLeft[maxIndex]++; + } + + double maxDistance = -1; + for (int i = 0; i < n - 1; i++) { + double diff = (stations[i + 1] - stations[i]); + double selectionLength = diff / (howManyLeft[i] + 1); + maxDistance = Math.max(maxDistance, selectionLength); + } + + return maxDistance; + } + + public double minimiseMaxDistanceHeap(int[] stations, int k) { + int n = stations.length; + int[] howManyLeft = new int[n - 1]; + + var priorityQueue = new java.util.PriorityQueue((a, b) -> Double.compare(b[0], a[0])); + + for (int i = 0; i < n - 1; i++) { + double diff = (stations[i + 1] - stations[i]); + priorityQueue.add(new double[]{diff, i}); + } + + for (int gasStations = 1; gasStations <= k && !priorityQueue.isEmpty(); gasStations++) { + double[] curr = priorityQueue.poll(); + int index = (int) curr[1]; + howManyLeft[index]++; + double diff = stations[index + 1] - stations[index]; + double selectionLength = diff / (howManyLeft[index] + 1); + priorityQueue.add(new double[]{selectionLength, index}); + } + return priorityQueue.peek()[0]; + } + + //Minimum possible answer: We will get the minimum answer when we place all the gas stations in a single location. + // Now, in this case, the maximum distance will be 0. + //Maximum possible answer: We will not place stations before the first or after the last station rather we will place stations in between the existing stations. + // So, the maximum possible answer is the maximum distance between two consecutive existing stations. + public double minimiseMaxDistance(int[] stations, int k) { + int n = stations.length; // size of the array + double left = 0; + double right = 0; + + for (int i = 0; i < n - 1; i++) { + right = Math.max(right, stations[i + 1] - stations[i]); + } + + double diff = 1e-6; + + //The condition 'while(low <= high)' inside the 'while' loop won't work for decimal answers, and using it might lead to a TLE error. + // To avoid this, we can modify the condition to 'while(high - low > 10^(-6))'. + // This means we will only check numbers up to the 6th decimal place. + while (right - left < diff) { + double mid = (left + right) / (2.0); + int count = numberOfGasStationsRequired(stations, mid); + //low = mid+1: We have used this operation to eliminate the left half. + // But if we apply the same here, we might ignore several decimal numbers and possibly our actual answer. + // So, we will use this: low = mid. + if (count > k) { + //If result > k: On satisfying this condition, we can conclude that the number ‘mid’ is smaller than our answer. + // So, we will eliminate the left half and consider the right half + left = mid; + } else { + right = mid; + } + } + return right; + } + + private int numberOfGasStationsRequired(int[] arr, double mid) { + int n = arr.length; + int cnt = 0; + for (int i = 1; i < n; i++) { + //For each section between i and i-1, we will do the following: + //No. of stations = (arr[i]-arr[i-1]) / dist + int numberInBetween = (int) ((arr[i] - arr[i - 1]) / mid); + //Let's keep in mind a crucial edge case: if the section_length (arr[i] - arr[i-1]) is completely divisible by 'dist', + // the actual number of stations required will be one less than what we calculate. + //if (arr[i]-arr[i-1] == (No. of stations*dist): No. of stations -= 1. + if ((arr[i] - arr[i - 1]) == (mid * numberInBetween)) { + numberInBetween--; + } + cnt += numberInBetween; + } + return cnt; + } +} diff --git a/src/main/java/binarysearch/MedianOfTwoSortedArrays.java b/src/main/java/binarysearch/MedianOfTwoSortedArrays.java new file mode 100644 index 0000000..04b59d5 --- /dev/null +++ b/src/main/java/binarysearch/MedianOfTwoSortedArrays.java @@ -0,0 +1,137 @@ +package binarysearch; + +import java.util.Comparator; +import java.util.PriorityQueue; + +/** + * https://www.youtube.com/watch?v=F9c7LpRZWVQ + * TODO revise + * tricky binary search + */ +public class MedianOfTwoSortedArrays { + + public static void main(String[] args) { + int[] x = {1, 3, 8, 9, 15, 17, 30}; + int[] y = {7, 11, 18, 19, 21, 25}; + // 1,3,7,8,9,11,15,18,19,21,25 + MedianOfTwoSortedArrays mm = new MedianOfTwoSortedArrays(); + System.out.println(mm.findMedianSortedArraysEff(x, y) + " - " + mm.findMedianSortedArrays(x, y)); + } + + /** + * Each half contains (n/2) elements. + * Each half also contains x elements from the first array i.e. arr1[] and (n/2)-x elements from the second array i.e. arr2[]. + * The value of x might be different for the two halves. + * For example, in the above array, the left half contains 3 elements from arr1[] and 2 elements from arr2[]. + * + * @param input1 + * @param input2 + * @return + */ + public double findMedianSortedArraysBruteForce(int[] input1, int[] input2) { + int[] merged = new int[input1.length + input2.length]; + int i = 0; + int j = 0; + int k = 0; + while (i < input1.length && j < input2.length) { + if (input1[i] < input2[j]) { + merged[k++] = input1[i++]; + } else { + merged[k++] = input2[j++]; + } + } + while (i < input1.length) { + merged[k++] = input1[i++]; + } + while (j < input2.length) { + merged[k++] = input2[j++]; + } + + if (merged.length % 2 == 0) { + return (merged[merged.length / 2] + merged[merged.length / 2 - 1]) / 2.0; + } else { + return merged[merged.length / 2]; + } + } + + public double findMedianSortedArrays(int[] input1, int[] input2) { + //if input1 length is greater than switch them so that input1 is smaller than input2. + // the reason is to do binary search on smaller array to reduce time complexity + if (input1.length > input2.length) { + return findMedianSortedArrays(input2, input1); + } + int x = input1.length; + int y = input2.length; + int n = x + y; + int leftHalf = (n + 1) / 2; // the reason is this works for both odd and even + // if the n is 10, 10+1/2 = median is 5, if n is 9, 9+1/2 = median 5 + + // the whole idea is to partition the 2 arrays so that the left side and right side have same number of elements + // let's take example A= 1,3,7 and B= 2,6,8,9,10 + + // first take (low + high) / 2 for A it'd be 1 in this example. we partition at 1, [1 ||, 3,7] (1 element on left and 2 on right) + // for B we need to do this, {(x + y + 1) / 2 - partitionX} = (3+5+1/2)-1=3 [2,6,8||,9,10] (3 elements on left and 2 on right) + // add the total left and right elements for both arrays would come to be equal 3+1(left partition) and 2+2(right partition) + + int low = 0; + int high = x; + while (low <= high) { + int mid1 = (low + high) / 2; + int mid2 = leftHalf - mid1; + + int left1 = (mid1 == 0) ? Integer.MIN_VALUE : input1[mid1 - 1]; + int right1 = (mid1 == x) ? Integer.MAX_VALUE : input1[mid1]; + int left2 = (mid2 == 0) ? Integer.MIN_VALUE : input2[mid2 - 1]; + int right2 = (mid2 == y) ? Integer.MAX_VALUE : input2[mid2]; + + if (left1 <= right2 && left2 <= right1) { + if (n % 2 == 0) { + return (Math.max(left1, left2) + Math.min(right1, right2)) / 2.0; + } else { + return Math.max(left1, left2); + } + } else if (left1 > right2) { + //If l1 > r2: This implies that we have considered more elements from arr1[] than necessary. + // So, we have to take less elements from arr1[] and more from arr2[]. + // In such a scenario, we should try smaller values of x. + // To achieve this, we will eliminate the right half (high = mid-1). + high = mid1 - 1; + } + //This implies that we have considered more elements from arr2[] than necessary. + // So, we have to take less elements from arr2[] and more from arr1[]. + // In such a scenario, we should try bigger values of x. + // To achieve this, we will eliminate the left half (low = mid+1). + else low = mid1 + 1; + + } + return -1.0; + } + + public double findMedianSortedArraysEff(int[] nums1, int[] nums2) { + + PriorityQueue minHeap = new PriorityQueue<>(); + PriorityQueue maxHeap = new PriorityQueue<>(Comparator.reverseOrder()); + + + for (int value : nums1) { + maxHeap.add(value); + } + + for (int i : nums2) { + maxHeap.add(i); + } + + int val = 0; + //division refers to integer division, this modified formula (n1+n2+1) / 2 will be valid for both cases of odd and even. + val = (nums1.length + nums2.length + 1) / 2; + for (int k = 0; k < val; k++) { + minHeap.add(maxHeap.poll()); + } + + if (minHeap.size() == maxHeap.size()) { + return (double) (minHeap.peek() + maxHeap.peek()) / 2; + } else { + return minHeap.peek(); + } + } +} diff --git a/src/main/java/binarysearch/NumberOfDaysToMakeMBouquets.java b/src/main/java/binarysearch/NumberOfDaysToMakeMBouquets.java new file mode 100644 index 0000000..6e4c4b0 --- /dev/null +++ b/src/main/java/binarysearch/NumberOfDaysToMakeMBouquets.java @@ -0,0 +1,48 @@ +package binarysearch; + +import java.util.Arrays; + +/** + * https://leetcode.com/problems/minimum-number-of-days-to-make-m-bouquets/ + */ +public class NumberOfDaysToMakeMBouquets { + + public int minDays(int[] bloomDay, int m, int k) { + if ((long) m * k > bloomDay.length) { + return -1; + } + + int left=1; + int right= Arrays.stream(bloomDay).max().orElse(0); + + while(leftmid){ // if the day is greater, then break the window + window=0; + }else{ + window++; // increase the window size + if(window==k){ // if the window size is equal to K end the window, we get one bouquet and reset the window for next + bouquets++; + window=0; + } + } + } + return bouquets>=m; + } +} diff --git a/src/main/java/binarysearch/NumberOfRectangles.java b/src/main/java/binarysearch/NumberOfRectangles.java new file mode 100644 index 0000000..f8be65b --- /dev/null +++ b/src/main/java/binarysearch/NumberOfRectangles.java @@ -0,0 +1,65 @@ +package binarysearch; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + + +/** + * https://leetcode.com/problems/count-number-of-rectangles-containing-each-point + * the constraints are + * 1 <= rectangles.length, points.length <= 5 * 10^4 + * 1 <= li, xj <= 10^9 + * 1 <= hi, yj <= 100 + * This tells us that we cannot go through all the rectangles for all the points, + * coz that will be 5 * 10^4 * 5* 10^4 which would be > 10^8 operations, so would give TLE. + */ +public class NumberOfRectangles { + //We see that heights are only from 0 to 100. + // So can traverse in them. But lengths can be till 10^9 so have to do binary search in that. + public int[] countRectangles(int[][] rectangles, int[][] points) { + int[] res = new int[points.length]; + List> group = new ArrayList<>(101); + for(int i = 0; i < 101; i++){ + group.add(new ArrayList<>()); + } + + for(int[] rec : rectangles){ + int l = rec[0]; + int h = rec[1]; + group.get(h).add(l); + } + + for(int i = 0; i < 101; i++){ + Collections.sort(group.get(i)); + } + + for(int i = 0; i < points.length; i++){ + int count = 0; + int x = points[i][0]; + int y = points[i][1]; + for(int j = y; j < 101; j++){ + List cur = group.get(j); + int index = binarySearch(cur, x); + count += cur.size() - index; + } + res[i] = count; + } + + return res; + } + + private int binarySearch(List list, int x){ + int left = 0; + int right = list.size(); + while(left < right){ + int mid = left + (right - left) / 2; + if(list.get(mid) >= x){ + right = mid; + } else{ + left = mid + 1; + } + } + return left; + } +} diff --git a/src/main/java/binarysearch/PivotInteger.java b/src/main/java/binarysearch/PivotInteger.java new file mode 100644 index 0000000..aec4bf0 --- /dev/null +++ b/src/main/java/binarysearch/PivotInteger.java @@ -0,0 +1,26 @@ +package binarysearch; + +public class PivotInteger { + + public int pivotInteger(int n) { + + int total = (n * (n + 1)) / 2; + + int left = 0; + int right = n; + + while (left < right) { + int mid = (left + right) / 2; + + if ((mid * mid - total) < 0) { + left = mid + 1; + } else { + right = mid; + } + + } + + return left * left - total == 0 ? left : -1; + + } +} diff --git a/src/main/java/binarysearch/SearchAnElementInMatrix.java b/src/main/java/binarysearch/SearchAnElementInMatrix.java new file mode 100644 index 0000000..7c2f7e1 --- /dev/null +++ b/src/main/java/binarysearch/SearchAnElementInMatrix.java @@ -0,0 +1,87 @@ +package binarysearch; + +/** + * https://www.youtube.com/watch?v=FOa55B9Ikfg + * https://leetcode.com/problems/search-a-2d-matrix-ii/ + * https://leetcode.com/problems/search-a-2d-matrix/ + */ + +public class SearchAnElementInMatrix { + + private static boolean searchII(int[][] mat, int n, int x) { + + int i = 0; + int j = n - 1; // set indexes for top right element + // we can start from top right corner or bottom left corner, because from + // these points only we have 2 paths one increasing one decreasing + // if we start at 0,0 both ways are increasing order, so we cannot decide + // same with n,n both ways are decreasing order. + while (i < n && j >= 0) { + if (mat[i][j] == x) { + System.out.print("n Found at " + i + " " + j); + return true; + } + if (mat[i][j] > x) { + j--; + } else // if mat[i][j] < x + { + i++; + } + } + + System.out.print("n Element not found"); + return false; + + } + + public static boolean searchI(int[][] matrix, int target) { + if (matrix == null || matrix.length == 0) { + return false; + } + int start = 0; + int rows = matrix.length; + int cols = matrix[0].length; + int end = rows * cols - 1; + while (start <= end) { + int mid = (start + end) / 2; + /** + * Row calculation (mid / col): + * + * Think of it as "how many complete rows do we need to move down?" + * Each row contains col number of elements + * Integer division gives us the number of complete rows + * + * + * Column calculation (mid % col): + * + * Think of it as "after moving down the rows, how many steps do we need to move right in the current row?" + * The modulus operation gives us the remainder, which is the offset within the current row + * + * Imagine a bookshelf with 5 shelves (rows), each holding 10 books (columns). If you're told to get the 37th book: + * + * You'd move down 3 full shelves (37 / 10 = 3) + * Then move 7 books to the right on the 4th shelf (37 % 10 = 7) + */ + if (matrix[mid / cols][mid % cols] == target) { + return true; + } + if (matrix[mid / cols][mid % cols] < target) { + start = mid + 1; + } else { + end = mid - 1; + } + } + return false; + } + + public static void main(String[] args) { + int[][] mat = {{10, 20, 30, 40}, {15, 25, 35, 45}, {27, 29, 37, 48}, {32, 33, 39, 50}}; + + int[][] matI = {{1, 3, 5, 7}, {10, 11, 16, 20}, {23, 30, 34, 50}}; + + System.out.println(searchI(matI, 11)); + System.out.println(searchII(mat, 4, 33)); + + } + +} diff --git a/src/main/java/binarysearch/SearchElementInSortedAndRotatedArray.java b/src/main/java/binarysearch/SearchElementInSortedAndRotatedArray.java new file mode 100644 index 0000000..e65aa9c --- /dev/null +++ b/src/main/java/binarysearch/SearchElementInSortedAndRotatedArray.java @@ -0,0 +1,76 @@ +package binarysearch; + +/** + * https://leetcode.com/problems/search-in-rotated-sorted-array/ + * https://leetcode.com/problems/search-in-rotated-sorted-array-ii/ + */ +public class SearchElementInSortedAndRotatedArray { + + public static void main(String[] args) { + int[] arr = {4, 5, 6, 7, 0, 1, 2}; + + SearchElementInSortedAndRotatedArray search = new SearchElementInSortedAndRotatedArray(); + System.out.println(search.searchI(arr, 0)); + + int[] nums = {2, 5, 6, 0, 0, 1, 2}; + System.out.println(search.searchII(nums, 2)); + } + + // 4, 5, 0, 1, 2, 3 + public int searchI(int[] nums, int target) { + int start = 0, end = nums.length - 1; + while (start <= end) { + int mid = start + (end - start) / 2; + if (nums[mid] == target) return mid; + + else if (nums[mid] >= nums[start]) { + if (target >= nums[start] && target < nums[mid]) { + end = mid - 1; + } + else { + start = mid + 1; + } + } + else { + if (target <= nums[end] && target > nums[mid]) { + start = mid + 1; + } + else { + end = mid - 1; + } + } + } + return -1; + } + + // with duplicates + //[2,5,6,0,0,1,2] + // 0 + //duplicates, we know nums[mid] != target, so nums[start] != target //based on current information, + // we can only move left pointer to skip one cell + // thus in the worst case, we would have target: 2, and array like 11111111, + // then the running time would be O(n) + + public boolean searchII(int[] nums, int target) { + int left = 0, right = nums.length - 1, mid; + + while (left <= right) { + mid = (left + right) / 2; + if (nums[mid] == target) return true; + + // the only difference from the first one, tricky case, just update left and right + if ((nums[left] == nums[mid]) && (nums[right] == nums[mid])) { + ++left; + --right; + } else if (nums[left] <= nums[mid]) { + if ((nums[left] <= target) && (nums[mid] > target)) right = mid - 1; + else left = mid + 1; + } else { + if ((nums[mid] < target) && (nums[right] >= target)) left = mid + 1; + else right = mid - 1; + } + } + return false; + } + +} diff --git a/src/main/java/binarysearch/SearchInsertPosition.java b/src/main/java/binarysearch/SearchInsertPosition.java new file mode 100644 index 0000000..0d68572 --- /dev/null +++ b/src/main/java/binarysearch/SearchInsertPosition.java @@ -0,0 +1,22 @@ +package binarysearch; + +/** + * https://leetcode.com/problems/search-insert-position/ + */ +public class SearchInsertPosition { + + public int searchInsert(int[] nums, int target) { + int left = 0; + //Also notice that the input target might be larger than all elements in nums and therefore needs to placed at the end of the array. + // That's why we should initialize right = len(nums) instead of right = len(nums) - 1. + int right = nums.length; // search space is [0, len] since insert index could be after last element + + while (left < right) { + int mid = left + (right - left) / 2; + if (nums[mid] >= target) right = mid; + else left = mid + 1; + } + + return left; + } +} diff --git a/src/main/java/binarysearch/ShipPackageWithNDays.java b/src/main/java/binarysearch/ShipPackageWithNDays.java new file mode 100644 index 0000000..c905235 --- /dev/null +++ b/src/main/java/binarysearch/ShipPackageWithNDays.java @@ -0,0 +1,54 @@ +package binarysearch; + +import java.util.Arrays; + +/** + * https://leetcode.com/problems/capacity-to-ship-packages-within-d-days + */ +public class ShipPackageWithNDays { + public static int shipWithinDays(int[] weights, int D) { + if (weights.length == 0 || D == 0) return 0; + + // this is because the ship should carry atleast the max weight + // else the max would be rejected + int lowerBound = Arrays.stream(weights).max().orElse(0); + int upperBound = Arrays.stream(weights).sum(); + int mid = 0; + while (lowerBound < upperBound) { + mid = (lowerBound + upperBound) / 2; + int daysToCarry = binarySearchUtil(weights, mid); + // if mid can carry weight then increase the upperBound to mid + if (daysToCarry<=D) upperBound = mid; + else lowerBound = mid + 1; + + } + + return lowerBound; + } + + + public static int binarySearchUtil(int[] weights, int mid) { + int sum = 0; // weight of current ship + int currentDay = 1; // number of days estimated + for (int weight : weights) { + if (sum + weight > mid) { + currentDay += 1; + sum = 0; + + } + sum += weight; + + // currentWeight += weight + //if (currentWeight > estimatedCapacity) { + // estimatedDays += 1 // current weight on the ship over estimated capacity so we need one more day + // currentWeight = weight // move the latest over weighted package to the ship on the following day + // } + } + + return currentDay; + } + + public static void main(String[] args) { + System.out.println(shipWithinDays(new int[]{1,2,3,4,5,6,7,8,9,10}, 5)); + } +} \ No newline at end of file diff --git a/src/main/java/binarysearch/SingleElementInSortedArray.java b/src/main/java/binarysearch/SingleElementInSortedArray.java new file mode 100644 index 0000000..26f5f00 --- /dev/null +++ b/src/main/java/binarysearch/SingleElementInSortedArray.java @@ -0,0 +1,38 @@ +package binarysearch; + +/** + * https://leetcode.com/problems/single-element-in-a-sorted-array + */ +public class SingleElementInSortedArray { + + // the main idea here is the single element flips the order of the duplicates + // for example in [1,1,2,2,3,4,4,8,8] before 3 the order is (even,odd) after 3 the order is (odd,even) + public int singleNonDuplicate(int[] nums) { + if (nums.length == 1) + return nums[0]; + + int left = 1; + int right = nums.length - 1; + if(nums[0]!=nums[1]) return nums[0]; + if(nums[nums.length-1]!=nums[nums.length-2]) return nums[nums.length-1]; + while (left <= right) { + int mid = left + (right - left) / 2; + //base case + if (nums[mid - 1] != nums[mid] && nums[mid + 1] != nums[mid]) { + return nums[mid]; + } + // if mid is odd and mid-1 is equal to mid then the single element is on the left + // if mid is even and mid+1 is equal to mid then the single element is on the left + //[1,1,2,2,3,4,4,8,8] here if the mid is odd say 3 we check the mid-1 + // if the mid is even we check the mid+1 if the condition meets then we haven't + // seen the single element which flips the order of the duplicates + if((mid%2==1 && nums[mid] == nums[mid-1]) || (mid%2==0 && nums[mid]==nums[mid+1])){ + left=mid+1; + }else{ + right=mid-1; + } + } + + return -1; + } +} \ No newline at end of file diff --git a/src/main/java/binarysearch/SmallestDivisor.java b/src/main/java/binarysearch/SmallestDivisor.java new file mode 100644 index 0000000..2b4e8b9 --- /dev/null +++ b/src/main/java/binarysearch/SmallestDivisor.java @@ -0,0 +1,33 @@ +package binarysearch; + +import java.util.Arrays; + +// https://leetcode.com/problems/find-the-smallest-divisor-given-a-threshold/ +public class SmallestDivisor { + + public int smallestDivisor(int[] nums, int threshold) { + int left=1; + int right= Arrays.stream(nums).max().orElse(0); + + while(leftlimit){ + partition++; + System.out.println("isFeasible limit is:: "+ limit +" "+ "but sum is " + sum + " so increasing partition "+partition); + sum=num; + } + + } + System.out.println("End of isFeasible:: sum is "+ sum + " and partition is "+partition+" and m is "+m); + return partition<=m; + } + + public static void main(String[] args) { + System.out.println(new SubArraySplitSum().splitArray(new int[]{7,2,5,10,8}, 2)); + //System.out.println(new SubArraySplitSum().splitArray(new int[]{1,4,4}, 3)); + } +} diff --git a/src/main/java/binarysearch/SumMutatedArrayCloseTarget.java b/src/main/java/binarysearch/SumMutatedArrayCloseTarget.java new file mode 100644 index 0000000..ce37e24 --- /dev/null +++ b/src/main/java/binarysearch/SumMutatedArrayCloseTarget.java @@ -0,0 +1,44 @@ +package binarysearch; + +import java.util.Arrays; + +/** + * https://leetcode.com/problems/sum-of-mutated-array-closest-to-target/ + */ +public class SumMutatedArrayCloseTarget { + Integer closest = Integer.MAX_VALUE; + Integer minElem = 0; + + public int findBestValue(int[] arr, int target) { + int left = 0; + int right = Arrays.stream(arr).max().orElse(0) + 1; // +1 is to extend the window slightly to overcome edge cases + + while (left < right) { + int mid = left + (right - left) / 2; + if (isConditionSatisfies(mid, arr, target)) { + right = mid; + } else { + left = mid + 1; + + } + } + + return minElem; + } + + public boolean isConditionSatisfies(int mid, int[] arr, int target) { + int total = 0; + for (int j : arr) { + total += Math.min(j, mid); + + } + if (Math.abs(target - total) < closest) { + closest = Math.abs(target - total); + minElem = mid; + } else if (Math.abs(target - total) == closest) { + minElem = Math.min(minElem, mid); + } + + return total >= target; + } +} diff --git a/src/main/java/bitwise/AddNumbers.java b/src/main/java/bitwise/AddNumbers.java new file mode 100644 index 0000000..302057b --- /dev/null +++ b/src/main/java/bitwise/AddNumbers.java @@ -0,0 +1,24 @@ +package bitwise; + +// https://leetcode.com/problems/add-binary/ +public class AddNumbers { + public String addBinary(String a, String b) { + StringBuilder sb = new StringBuilder(); + int i = a.length()-1; + int j = b.length()-1; + int carry = 0; + while (i >= 0 || j >= 0 || carry == 1) { + if (i >= 0) { + carry += a.charAt(i--) - '0'; + } + if (j >= 0) { + carry += b.charAt(j--) - '0'; + } + sb.append(carry % 2); + carry /= 2; + } + + return sb.reverse().toString(); + } + +} diff --git a/src/main/java/bitwise/BitwiseRange.java b/src/main/java/bitwise/BitwiseRange.java new file mode 100644 index 0000000..e7aaef3 --- /dev/null +++ b/src/main/java/bitwise/BitwiseRange.java @@ -0,0 +1,31 @@ +package bitwise; + +// https://leetcode.com/problems/bitwise-and-of-numbers-range/ +public class BitwiseRange { + + + // https://www.youtube.com/watch?v=j3XRFREnPWI + // the reason here is there is a common prefix between the two numbers + // we find the common prefix and then shift it to the left + // this is a property of bitwise and + + public static int rangeBitwiseAnd(int left, int right) { + int shift = 0; + System.out.println("le: " + Integer.toBinaryString(left)); + System.out.println("ri: " + Integer.toBinaryString(right)); + System.out.println("#########"); + while (left < right) { + left >>= 1; + right >>= 1; + System.out.println("le : " + Integer.toBinaryString(left)); + System.out.println("ri : " + Integer.toBinaryString(right)); + shift++; + } + + return left << shift; + } + + public static void main(String[] args) { + System.out.println(rangeBitwiseAnd(1024, 512)); + } +} diff --git a/src/main/java/bitwise/ExcellentList.java b/src/main/java/bitwise/ExcellentList.java new file mode 100644 index 0000000..b6d6308 --- /dev/null +++ b/src/main/java/bitwise/ExcellentList.java @@ -0,0 +1,51 @@ +package bitwise; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +// https://leetcode.com/problems/number-of-excellent-pairs/ +public class ExcellentList { + /** + https://leetcode.com/problems/number-of-excellent-pairs/solutions/2370663/c-logic-explained-with-pictures/ + * If there are x set bits in a number and y set bits in b number + * then the OR component is x+y-k where k is the number of set bits common in a&b + * the AND component is k + * so the final answer (AND+OR) is x+y-k+k = x+y + * @param nums + * @param k + * @return + */ + public long countExcellentPairs(int[] nums, int k) { + long[] count = new long[30]; // 30 instead of 32 because the constraint of the problem is + // 1E9 which can be considered as 2^30 + Set set = Arrays.stream(nums).collect(HashSet::new, Set::add, Set::addAll); + for (int a: set){ + count[Integer.bitCount(a)]++; + } + long result = 0L; + for (int i=1;i<30;++i){ + for (int j=1;j<30;++j){ + if (i+j >= k){ + // the reason we are multiplying the count[i] and count[j] is because + // if we have 2 elements with i bits and 3 elements with j bits, then + // we can have 2*3 = 6 excellent pairs + /** + * Array: nums = [4, 6, 7, 10] + * k = 5 + * Count Array: + * count[2] = 2 (two numbers with 2 1s: 4 and 6) + * count[3] = 1 (one number with 3 1s: 7) + * count[4] = 1 (one number with 4 1s: 10) + * Multiplication: + * count[2] * count[3] = 2 * 1 = 2 (pairs: (4, 7), (6, 7)) + * count[2] * count[4] = 2 * 1 = 2 (pairs: (4, 10), (6, 10)) + * Total potential excellent pairs: 4 + */ + result+=count[i]*count[j]; + } + } + } + return result; + } +} diff --git a/src/main/java/bitwise/MaxProductString.java b/src/main/java/bitwise/MaxProductString.java new file mode 100644 index 0000000..fe0b165 --- /dev/null +++ b/src/main/java/bitwise/MaxProductString.java @@ -0,0 +1,48 @@ +package bitwise; + +public class MaxProductString { + + public int maxProduct(String[] words) { + int[] checker = new int[words.length]; + int max = 0; + // populating the checker array with their respective numbers + for (int i = 0; i < checker.length; i++) { + int num = 0; + for (int j = 0; j < words[i].length(); j++) { + + // a 1->1 + // b 2->10 + // c 4->100 + // ab 3->11 + // ac 5->101 + // abc 7->111 + // az 33554433->10000000000000000000000001 + + num |= 1 << (words[i].charAt(j) - 'a'); + //System.out.println(words[i].charAt(j)+"->"+num+ "->"+Integer.toBinaryString(num) ); + } + + checker[i] = num; + } + + for (int i = 0; i < words.length; i++) { + for (int j = i + 1; j < words.length; j++) { + // bitwise and operation between two numbers + // if the result is 0, then there is no common character + // if the result is not 0, then there is a common character + if ((checker[i] & checker[j]) == 0) + max = Math.max(max, words[i].length() * words[j].length()); + } + } + System.out.println(max); + return max; + } + + + public static void main(String[] args) { + MaxProductString m = new MaxProductString(); + m.maxProduct(new String[]{"abcw", "baz", "foo", "bar", "xtfn", "abcdef"}); + m.maxProduct(new String[]{"a", "ab", "abc", "d", "cd", "bcd", "abcd"}); + m.maxProduct(new String[]{"a", "aa", "aaa", "aaaa"}); + } +} \ No newline at end of file diff --git a/src/main/java/bitwise/Reverse.java b/src/main/java/bitwise/Reverse.java new file mode 100644 index 0000000..6097f1f --- /dev/null +++ b/src/main/java/bitwise/Reverse.java @@ -0,0 +1,14 @@ +package bitwise; + +// https://leetcode.com/problems/reverse-bits/ +public class Reverse { + + public int reverseBits(int n) { + int ans =0; + for(int i=0;i<=31;i++){ + int bit = n>>i & 1; + ans|=(bit<<31-i); + } + return ans; + } +} diff --git a/src/main/java/bitwise/SingleNumberII.java b/src/main/java/bitwise/SingleNumberII.java new file mode 100644 index 0000000..b2da3d4 --- /dev/null +++ b/src/main/java/bitwise/SingleNumberII.java @@ -0,0 +1,70 @@ +package bitwise; +// https://leetcode.com/problems/single-number-ii/ +// https://leetcode.com/problems/single-number-iii/ +public class SingleNumberII { + public int singleNumber(int[] nums) { + int ans = 0; + + for (int i = 0; i < 32; ++i) { + int sum = 0; + for (final int num : nums) { + // This step creates a bitmask pos where only the bit at + // position i is set to the value of sum + sum += num >> i & 1; + } + //Take the modulo of sum by 3: sum %= 3 + // This will clear the ith bit of ans if the corresponding bit of sum is 3. + // It will leave it as 1 if the corresponding bit of sum is 1. + sum %= 3; + + //Use the bitwise OR operation with ans and pos: ans |= pos. + // This sets the corresponding bit in ans to 1 + // if the bit at position i is part of an unbalanced line. + ans |= sum << i; + } + + return ans; + } + + /** + * High level: find XOR combo of two result. Then find one of them + * Step 1: XOR all numbers, the result will be res1 ^ res2 + * Step 2: traverse all 32 bit indexes of previous XOR result, once we find there exist 1 on a bit index, break + * the loop. Because one of the result at least have bit 1 on current bit index + * Step 3: traverse all numbers in the input array, if we find a number & the bit index we found at step 2 is not 0, + * then we can use res1 XOR current num to iteratively fill out effective bit in res1 + * (i.e. res1 ^ n1 ^ n1 ^ n2 ^ n2 = res1, if res1, n1 and n2 have bit 1 on the bitIndex + * Step 4: find another result number by using res1 ^ allNumberXOR (i.e. res1 ^ (res1 ^ res2 ^ ...)) = res2) + * */ + + public int[] singleNumberIII(int[] nums) { + int res1 = 0; + int res2 = 0; + + // step 1: find XOR combo of two numbers + int allNumXOR = 0; + for (int num : nums) { + allNumXOR ^= num; + } + + // step 2: find effective bit index of a number in one of two result numbers + int bitIndex; + for (bitIndex = 0; bitIndex < 32; bitIndex++) { + if ((allNumXOR & (1 << bitIndex)) != 0) { + break; + } + } + + // step 3: find first result + for (int num : nums) { + if ((num & (1 << bitIndex)) != 0) { + // current bitIndex, we only want that single one, + // and cancel all the rest of numbers by using XOR + res1 ^= num; + }else{ + res2 ^= num; + } + } + return new int[]{res1, res2}; + } +} diff --git a/src/main/java/bitwise/Subset.java b/src/main/java/bitwise/Subset.java new file mode 100644 index 0000000..aa2519c --- /dev/null +++ b/src/main/java/bitwise/Subset.java @@ -0,0 +1,34 @@ +package bitwise; + +import java.util.ArrayList; +import java.util.List; + +public class Subset { + + public List> subsets(int[] nums) { + List> result = new ArrayList<>(); + int n=nums.length; + /* + + Iteration 1: num = 0 (000 in binary), loop skips. + Iteration 2: num = 1 (001), subset = [1]. + Iteration 3: num = 2 (010), subset = [2]. + Iteration 4: num = 3 (011), subset = [1, 3]. + Iteration 5: num = 4 (100), subset = [3]. + Iteration 6: num = 5 (101), subset = [2, 3]. + Iteration 7: num = 6 (110), subset = [1, 2]. + Iteration 8: num = 7 (111), subset = [1, 2, 3]. + */ + for (int i = 0; i < (1 << n); i++) { + ArrayList in = new ArrayList<>(); + for (int j = 0; j < n; j++) { + if ((i & (1 << j)) != 0) { + in.add(nums[j]); + } + } + result.add(in); + } + + return result; // total complexity: n.2^n + } +} diff --git a/src/main/java/cess/AppleDivision.java b/src/main/java/cess/AppleDivision.java new file mode 100644 index 0000000..ce81c69 --- /dev/null +++ b/src/main/java/cess/AppleDivision.java @@ -0,0 +1,31 @@ +package cess; + +/** + * Bitmask + *

+ * https://cses.fi/problemset/task/1623 + * https://www.youtube.com/watch?v=raGn3saVfa8 + */ +public class AppleDivision { + + public static long solve(int[] arr) { + long result = Long.MAX_VALUE; + int n = arr.length; + for (int mask = 0; mask < (1 << n); mask++) { + + long sumA = 0; + long sumB = 0; + + for (int pos = 0; pos < n; pos++) { + if ((mask & (1 << pos)) > 0) { + sumA += arr[pos]; + } else { + sumB += arr[pos]; + } + } + result = Math.min(result, Math.abs(sumA - sumB)); + + } + return result; + } +} diff --git a/src/main/java/cess/BinaryExponentiation.java b/src/main/java/cess/BinaryExponentiation.java new file mode 100644 index 0000000..eea8ba6 --- /dev/null +++ b/src/main/java/cess/BinaryExponentiation.java @@ -0,0 +1,20 @@ +package cess; + +public class BinaryExponentiation { + + /** + * Fast method to compute exponent values a^b + * https://www.youtube.com/watch?v=L-Wzglnm4dM + */ + public int power(int a, int b) { + + int result = 1; + + while (b > 0) { + if (b == 1) result *= a; + a *= a; + b /= 2; + } + return result; + } +} diff --git a/src/main/java/cess/DigitQueries.java b/src/main/java/cess/DigitQueries.java new file mode 100644 index 0000000..afe8745 --- /dev/null +++ b/src/main/java/cess/DigitQueries.java @@ -0,0 +1,67 @@ +package cess; + +import java.util.Arrays; + +/** + * https://www.youtube.com/watch?v=QAcH8qD9Pe0 + * https://cses.fi/problemset/task/2431 + * + * too complex, debug to understand + */ +public class DigitQueries { + + public static String solve(long[] arr) { + long[] powerOfTen = new long[19]; + Arrays.fill(powerOfTen, 1); + + for (int i = 1; i < powerOfTen.length; i++) { + powerOfTen[i] = powerOfTen[i - 1] * 10; + + } + + for (long index : arr) { + + long digitsSoFar = 0; + long digitsBeforeActualBlock = 0; + int numberOfDigits = 0; + + for (int j = 1; j <= 18; j++) { + long distance = (powerOfTen[j] * powerOfTen[j - 1]) * j; + + digitsSoFar += distance; + + if (digitsSoFar >= index) { + numberOfDigits = j; + break; + } + digitsBeforeActualBlock += distance; + } + + long smallestValue = powerOfTen[numberOfDigits - 1]; + long largestValue = powerOfTen[numberOfDigits] - 1; + long bestValue = 0; + long bestValueStartPos = 0; + + while (smallestValue <= largestValue) { + long mid = (smallestValue + largestValue) / 2; + long startPostOfActual = digitsBeforeActualBlock + 1 + (mid - powerOfTen[numberOfDigits - 1]) * numberOfDigits; + if (startPostOfActual <= index) { + if (mid > bestValue) { + bestValue = mid; + bestValueStartPos = startPostOfActual; + } + smallestValue = mid + 1; + } else { + largestValue = mid - 1; + } + } + int res = (int) ((int) index - bestValueStartPos); + return String.valueOf(String.valueOf(bestValue).charAt(res)); + } + return ""; + } + + public static void main(String[] args) { + solve(new long[]{107}); + } +} diff --git a/src/main/java/cess/GreyCode.java b/src/main/java/cess/GreyCode.java new file mode 100644 index 0000000..9e2a04b --- /dev/null +++ b/src/main/java/cess/GreyCode.java @@ -0,0 +1,70 @@ +package cess; + + +import java.util.ArrayList; +import java.util.BitSet; +import java.util.List; + +/** + * https://www.youtube.com/watch?v=KOD2BFauQbA + * A Gray code is a list of all 2n + * bit strings of length n + * , where any two successive strings differ in exactly one bit (i.e., their Hamming distance is one). + * Your task is to create a Gray code for a given length n + *

+ *

+ * Input: + * 2 + *

+ * Output: + * 00 -| + * 01 -| - difference 1 + * 11 + * 10 + */ +public class GreyCode { + + public static List greyCode(int n) { + + if (n == 0) return new ArrayList<>(); + if (n == 1) { + List baseRes = new ArrayList<>(); + baseRes.add("0"); + baseRes.add("1"); + return baseRes; + } + + List intermediateResult = greyCode(n - 1); + List finalResult = new ArrayList<>(); + for (String s : intermediateResult) { + finalResult.add("0" + s); + } + + for (int i = intermediateResult.size() - 1; i >= 0; i--) { + finalResult.add("1" + intermediateResult.get(i)); + } + return finalResult; + + } + + public static List greyCode1(int n) { + List finalResult = new ArrayList<>(); + for (int i = 0; i < 1 << n; i++) { + int val = i ^ (i >> 1); + + // If len = 4 and val = 1, + // Integer.toBinaryString( (1 << len) | val ) => returns the string "10001", then + // "10001".substring( 1 ) discards the very first character. So we obtain what we want: + // "0001" + finalResult.add(Integer.toBinaryString((1 << n) | val).substring(1)); + } + return finalResult; + } + + public static void main(String[] args) { + BitSet b = new BitSet(); + greyCode(3).forEach(System.out::println); + System.out.println(); + greyCode1(3).forEach(System.out::println); + } +} diff --git a/src/main/java/cess/GridPath.java b/src/main/java/cess/GridPath.java new file mode 100644 index 0000000..dab0fb4 --- /dev/null +++ b/src/main/java/cess/GridPath.java @@ -0,0 +1,153 @@ +package cess; + +public class GridPath { + + public static boolean[][] onPath = new boolean[9][9]; + public static int[][] dirs = new int[][]{{-1, 0}, {0, 1}, {1, 0}, {0, -1}}; + static int[] p = new int[48]; + + /** + * Basic Algorithm + + * The first version of the algorithm does not contain any optimizations. We simply + * use backtracking to generate all possible paths from the upper-left corner to + * the lower-right corner and count the number of such paths. + + * Running time: 483 seconds + * Number of recursive calls: 76 billion + * + * + * Optimization 1 + * + * If the path reaches the lower-right square before it has visited all other + * squares of the grid, it is clear that it will not be possible to complete the + * solution. Using this observation, we can terminate the search immediately if we + * reach the lower-right square too early. + * + * Running time: 119 seconds + * Number of recursive calls: 20 billion + * + * + * If the path touches a wall and can turn either left or right, the grid splits + * into two parts that contain unvisited squares. In this case, we cannot visit all + * squares anymore, so we can terminate the search. + * + * Running time: 1.8 seconds + * Number of recursive calls: 221 million + * + * + * The idea of Optimization 2 can be generalized: if the path cannot continue + * forward but can turn either left or right, the grid splits into two parts that + * both contain unvisited squares. It is clear that we cannot visit all squares + * anymore, so we can terminate the search. + * + * Running time: 0.6 seconds + * Number of recursive calls: 69 million + * + * + * Now is a good moment to stop optimizing the algorithm and see what we have + * achieved. The running time of the original algorithm was $483$ seconds, and now + * after the optimizations, the running time is only $0.6$ seconds. Thus, the + * algorithm became nearly $1000$ times faster after the optimizations. + * + * Optimization 4 + * + * If the path creates a dead end that is not the bottom left corner, either the + * path will fail to visit all squares (the path may stop at the dead end or pass + * over it, sealing a square off) or the path will end in the wrong location. Thus, + * we want to avoid creating dead ends. For example, if the square to the left of + * our current location is blocked on three sides (including our current location), + * then the next step must be to the left in order to avoid creating a dead end. + * After this optimization, the program runs in under $1$ second. + */ + public int calculateGridPaths(String line) { + if (line.length() != 48) return -1; + + for (int i = 0; i < p.length; i++) { + char cur = line.charAt(i); + + if (cur == 'U') p[i] = 0; + else if (cur == 'R') p[i] = 1; + else if (cur == 'D') p[i] = 2; + else if (cur == 'L') p[i] = 3; + else p[i] = 4; // cur == '?' + } + + // set borders of grid + for (int i = 0; i < 9; i++) { + onPath[0][i] = true; + onPath[8][i] = true; + onPath[i][0] = true; + onPath[i][8] = true; + } + + return tryPath(0, 1, 1); + } + + public static int tryPath(int pathIdx, int curR, int curC) { + // Optimization 3 + if ((onPath[curR][curC - 1] && onPath[curR][curC + 1]) && + (!onPath[curR - 1][curC] && !onPath[curR + 1][curC])) + return 0; + if ((onPath[curR - 1][curC] && onPath[curR + 1][curC]) && + (!onPath[curR][curC - 1] && !onPath[curR][curC + 1])) + return 0; + + // Optimization 1 + if (curR == 7 && curC == 1) { // reached endpoint + if (pathIdx == p.length) return 1; // visited every cell -> valid! + return 0; // didn't visit every cell (path length is too short) + } + // visited all cells, but didn't end up in the correct location + if (pathIdx == p.length) return 0; + + int ret = 0; // cumulative count for this "starting position" + onPath[curR][curC] = true; + + // turn already determined, try going in that direction + if (p[pathIdx] < 4) { + int nxtR = curR + dirs[p[pathIdx]][0]; + int nxtC = curC + dirs[p[pathIdx]][1]; + if (!onPath[nxtR][nxtC]) { + ret += tryPath(pathIdx + 1, nxtR, nxtC); + } + } +// // now search for dead ends (Optimization 4) +// else if ((curC > 2) && onPath[curR][curC - 2] && +// (onPath[curR - 1][curC - 1] || onPath[curR + 1][curC - 1]) && +// (!onPath[curR][curC - 1])) { +// // potential dead end on the left: +// int nxtR = curR; +// int nxtC = curC - 1; +// ret += tryPath(pathIdx + 1, nxtR, nxtC); +// } else if ((curC < 6) && onPath[curR][curC + 2] && +// (onPath[curR - 1][curC + 1] || onPath[curR + 1][curC + 1]) && +// (!onPath[curR][curC + 1])) { +// // potential dead end on the right: +// int nxtR = curR; +// int nxtC = curC + 1; +// ret += tryPath(pathIdx + 1, nxtR, nxtC); +// } else if ((curR > 2) && onPath[curR - 2][curC] && +// onPath[curR - 1][curC - 1] && (!onPath[curR - 1][curC])) { +// // potential dead end upwards +// // note: I didn't include all possible scenarios because +// // it wasn't necessary in order for the program to run in time +// int nxtR = curR - 1; +// int nxtC = curC; +// ret += tryPath(pathIdx + 1, nxtR, nxtC); +// } + // iterate through all four possible turns + else { + for (int i = 0; i < 4; i++) { + int nxtR = curR + dirs[i][0]; + int nxtC = curC + dirs[i][1]; + if (onPath[nxtR][nxtC]) continue; + ret += tryPath(pathIdx + 1, nxtR, nxtC); + } + } + + // reset and return + onPath[curR][curC] = false; + return ret; + } +} diff --git a/src/main/java/cess/NumberFromSpiral.java b/src/main/java/cess/NumberFromSpiral.java new file mode 100644 index 0000000..6829749 --- /dev/null +++ b/src/main/java/cess/NumberFromSpiral.java @@ -0,0 +1,39 @@ +package cess; + +/** + * https://cses.fi/problemset/task/1071 + * https://www.youtube.com/watch?v=pNN35ZdX77Y + *

+ * tricky complex + */ +public class NumberFromSpiral { + + public int getValueAtIndex(int i, int j) { + + int max = Math.max(i, j); + + if ((max & 1) == 0) { + if (j == 1) { + return max * max; + } + + if (i < max) { + return getValueAtIndex(max, max) - (max - i); + } else if (i == max) { + return max * max - (j - 1); + } + } else { + if (i == 1) { + return max * max; + } + + if (j < max) { + return getValueAtIndex(max, max) - (max - j); + } else { + return max * max - (i - 1); + } + } + return -1; + } + +} diff --git a/src/main/java/cess/PalindromeReorder.java b/src/main/java/cess/PalindromeReorder.java new file mode 100644 index 0000000..ab3b984 --- /dev/null +++ b/src/main/java/cess/PalindromeReorder.java @@ -0,0 +1,49 @@ +package cess; + +/** + * https://cses.fi/problemset/task/1755 + * https://www.youtube.com/watch?v=ou8Xhp_YO8E&t=767s + */ +public class PalindromeReorder { + + public static String reorderToPalindrome(String s) { + + int[] arr = new int[26]; + + for (char c : s.toCharArray()) { + arr[c - 'A']++; + } + + int oddCharPos = -1; + + for (int i = 0; i < 26; i++) { + if (arr[i] == 1 && oddCharPos != -1) { + return ""; + } else if (arr[i] == 1 && oddCharPos == -1) { + oddCharPos = i; + } + } + + if (oddCharPos == -1 && s.length() % 2 == 1) return ""; + if (oddCharPos != -1 && s.length() % 2 == 0) return ""; + + StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < 26; i++) { + for (int j = 0; j < arr[i] / 2; j++) { + sb.append(Character.toString(i + 'A')); + } + } + + String s1 = sb.reverse().toString(); + sb.reverse(); + if (oddCharPos != -1) { + sb.append(Character.toString(oddCharPos + 'A')); + } + return sb.append(s1).toString(); + } + + public static void main(String[] args) { + System.out.println(reorderToPalindrome("AAAACACBA")); + } +} diff --git a/src/main/java/cess/TowerOfHanoi.java b/src/main/java/cess/TowerOfHanoi.java new file mode 100644 index 0000000..5bbf0fa --- /dev/null +++ b/src/main/java/cess/TowerOfHanoi.java @@ -0,0 +1,20 @@ +package cess; + +public class TowerOfHanoi { + + public static void solve(String from, String to, String aux, int n) { + if (n == 1) { + System.out.println("Take disk 1 from rod " + from + " to rod " + to); + return; + } + solve(from, aux, to, n - 1); + System.out.println("Take disk " + n + " from rod " + from + " to rod " + to); + + System.out.println(); + solve(aux, to, from, n - 1); + } + + public static void main(String[] args) { + solve("A", "C", "B", 3); + } +} diff --git a/src/main/java/cess/TwoKnights.java b/src/main/java/cess/TwoKnights.java new file mode 100644 index 0000000..1eba0b7 --- /dev/null +++ b/src/main/java/cess/TwoKnights.java @@ -0,0 +1,22 @@ +package cess; + +/** + * https://cses.fi/problemset/task/1072 + *

+ * https://www.youtube.com/watch?v=nKVubpav6Uk + */ +public class TwoKnights { + + public int getNumberOfWays(int n) { + + // number af all possibilities to place 2 knights on n*n board + // binomial formula + int numberOfAllPossibilities = (((n * n) * (n * n) - 1) / 2); + + //number Of rectangles Where two knights can attack each other + int noOfRect = 4 * (n - 1) * (n - 2); + + // this return no of ways the two knights cannot attack each other + return numberOfAllPossibilities - noOfRect; + } +} diff --git a/src/main/java/cess/sort_search/Apartments.java b/src/main/java/cess/sort_search/Apartments.java new file mode 100644 index 0000000..ffae1ec --- /dev/null +++ b/src/main/java/cess/sort_search/Apartments.java @@ -0,0 +1,33 @@ +package cess.sort_search; + +import java.util.Arrays; + +public class Apartments { + + public static int search(int[] memberReq, int[] apartments, int diff) { + Arrays.sort(apartments); + Arrays.sort(memberReq); + int i = 0; + int j = 0; + int ans = 0; + for (; i < memberReq.length; i++) { + // if the curr space is less, then try finding a suitable apartment for applicant i + while (j < apartments.length && apartments[j] < memberReq[i] - diff) { + j++; + } + if (Math.abs(apartments[j] - memberReq[i]) <= diff) { + ans++; + i++; + j++; + } else { + // if it's large then move to next applicant + i++; + } + } + return ans; + } + + public static void main(String[] args) { + search(new int[]{60,45,80,60},new int[]{30,60,75}, 5); + } +} diff --git a/src/main/java/cess/sort_search/MovieFestival.java b/src/main/java/cess/sort_search/MovieFestival.java new file mode 100644 index 0000000..e3d4cfc --- /dev/null +++ b/src/main/java/cess/sort_search/MovieFestival.java @@ -0,0 +1,41 @@ +package cess.sort_search; + +import graph.leetcode.Pair; + +import java.util.*; + +// similar to intervals problem but with a twist +public class MovieFestival { + + public static int moviesWatchedCount(int[][] movieTimings, int persons){ + int unWatchableCount = 0; + Arrays.sort(movieTimings, Comparator.comparingInt(a -> a[1])); + TreeSet> set = new TreeSet<>(Comparator.comparingInt(a -> a.key)); + + for(int i=0;i(movieTimings[i][1]*-1,i)); + continue; + } + Pair p = set.lower(new Pair<>(movieTimings[i][0]*-1,-1)); + if (p!=null && !Objects.equals(p.value, set.last().value)){ + set.remove(p); + set.add(new Pair<>(movieTimings[i][1]*-1,i)); + continue; + } + if (set.size() < persons){ + set.add(new Pair<>(movieTimings[i][1]*-1,i)); + }else { + unWatchableCount++; + } + + } + + return movieTimings.length - unWatchableCount; + } + + public static void main(String[] args) { + System.out.println(moviesWatchedCount(new int[][]{{1,5},{8,10},{3,6},{2,5},{6,9}},2)); + } + +} diff --git a/src/main/java/cess/sort_search/RestaurantCustomers.java b/src/main/java/cess/sort_search/RestaurantCustomers.java new file mode 100644 index 0000000..bb8e8d3 --- /dev/null +++ b/src/main/java/cess/sort_search/RestaurantCustomers.java @@ -0,0 +1,29 @@ +package cess.sort_search; + +import java.util.Map; +import java.util.TreeMap; + +/** + * https://cses.fi/problemset/task/1619 + * https://www.youtube.com/watch?v=O9Sptr-RdRo + */ +public class RestaurantCustomers { + + public int maxCustomersAtGivenTime(int[][] arrivalTimes){ + Map cache = new TreeMap<>(); + + for (int[] arr:arrivalTimes) { + cache.put(arr[0],1); + cache.put(arr[1],-1); + } + int ans =0; + int count =0; + for (Map.Entry entry : cache.entrySet()){ + + ans = Math.max(ans,count+entry.getValue()); + } + + return ans; + + } +} diff --git a/src/main/java/cess/sort_search/SubsetSumCoin.java b/src/main/java/cess/sort_search/SubsetSumCoin.java new file mode 100644 index 0000000..1578ec5 --- /dev/null +++ b/src/main/java/cess/sort_search/SubsetSumCoin.java @@ -0,0 +1,31 @@ +package cess.sort_search; + +import java.util.Arrays; + +public class SubsetSumCoin { + + public static int sumCannotFormedBySubset(int[] arr) { + int res = 1; // Initialize result + + // sort the input array + Arrays.sort(arr); + + // Traverse the array and increment 'res' if arr[i] is + // smaller than or equal to 'res'. + for (int i = 0; i < arr.length; i++) + { + if(arr[i] > res){ + return res; + } + else{ + res+=arr[i]; + } + } + + return res; + } + + public static void main(String[] args) { + System.out.println(sumCannotFormedBySubset(new int[]{2,9,1,2,7})); + } +} diff --git a/src/main/java/combinationsandpermutations/BeautifulArrangements.java b/src/main/java/combinationsandpermutations/BeautifulArrangements.java new file mode 100644 index 0000000..34cb4ea --- /dev/null +++ b/src/main/java/combinationsandpermutations/BeautifulArrangements.java @@ -0,0 +1,26 @@ +package combinationsandpermutations; + +/** + * https://leetcode.com/problems/beautiful-arrangement + */ +public class BeautifulArrangements { + public int countArrangement(int n) { + boolean[] visited = new boolean[n + 1]; + return count(visited, n, n); + } + + private int count(boolean[] visited, int index, int n) { + if (index == 0) { + return 1; + } + int count = 0; + for (int i = 1; i <= n; i++) { + if ((index % i == 0 || i % index == 0) && !visited[i]) { + visited[i] = true; + count += count(visited, index - 1, n); + visited[i] = false; + } + } + return count; + } +} diff --git a/src/main/java/combinationsandpermutations/CanPartitionKSubSets.java b/src/main/java/combinationsandpermutations/CanPartitionKSubSets.java new file mode 100644 index 0000000..8f49efa --- /dev/null +++ b/src/main/java/combinationsandpermutations/CanPartitionKSubSets.java @@ -0,0 +1,37 @@ +package combinationsandpermutations; + +import java.util.Arrays; + +/** + * Tricky TODO revise + * https://leetcode.com/problems/partition-to-k-equal-sum-subsets/ + * + * Ref: http://www.geeksforgeeks.org/partition-set-k-subsets-equal-sum/ + */ +public class CanPartitionKSubSets { + public boolean canPartitionKSubsets(int[] A, int k) { + if (k > A.length) return false; + int sum = 0; + for (int num : A) sum += num; + if (sum % k != 0) return false; + boolean[] visited = new boolean[A.length]; + Arrays.sort(A); + return dfs(A, 0, A.length - 1, visited, sum / k, k); + } + + public boolean dfs(int[] A, int sum, int st, boolean[] visited, int target, int round) { + if (round == 0) return true; + + if (sum == target && dfs(A, 0, A.length - 1, visited, target, round - 1)) + return true; + for (int i = st; i >= 0; --i) { + if (!visited[i] && sum + A[i] <= target) { + visited[i] = true; + if (dfs(A, sum + A[i], i - 1, visited, target, round)) + return true; + visited[i] = false; + } + } + return false; + } +} diff --git a/src/main/java/combinationsandpermutations/CombinationIterator.java b/src/main/java/combinationsandpermutations/CombinationIterator.java new file mode 100644 index 0000000..352b1c8 --- /dev/null +++ b/src/main/java/combinationsandpermutations/CombinationIterator.java @@ -0,0 +1,54 @@ +package combinationsandpermutations; + +// CombinationIterator iterator = new CombinationIterator("abc", 2); // creates the iterator. + +import java.util.ArrayDeque; +import java.util.Deque; + +// iterator.next(); // returns "ab" +// iterator.hasNext(); // returns true +// iterator.next(); // returns "ac" +// iterator.hasNext(); // returns true +// iterator.next(); // returns "bc" +// iterator.hasNext(); // returns false +public class CombinationIterator { + Deque deque; + boolean[] visited; + + public CombinationIterator(String characters, int combinationLength) { + deque = new ArrayDeque<>(); + visited = new boolean[characters.length()]; + generateCombinations(characters, deque, new StringBuilder(), combinationLength, 0); + } + + public String next() { + if (hasNext()) return deque.poll(); + + return ""; + } + + public boolean hasNext() { + return !deque.isEmpty(); + } + + public void generateCombinations(String characters, Deque deque, StringBuilder sb, int limit, int start) { + + if (sb.length() == limit) { + deque.offer(sb.toString()); + return; + } + + for (int i = start; i < characters.length(); i++) { + sb.append(characters.charAt(i)); + generateCombinations(characters, deque, sb, limit, i + 1); + sb.deleteCharAt(sb.length() - 1); + } + } + + public static void main(String[] args) { + CombinationIterator cb = new CombinationIterator("abc", 2); + System.out.println(cb.next()); + System.out.println(cb.next()); + System.out.println(cb.next()); + } +} \ No newline at end of file diff --git a/src/main/java/combinationsandpermutations/CombinationSum.java b/src/main/java/combinationsandpermutations/CombinationSum.java new file mode 100644 index 0000000..327eb8e --- /dev/null +++ b/src/main/java/combinationsandpermutations/CombinationSum.java @@ -0,0 +1,125 @@ +package combinationsandpermutations; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class CombinationSum { + + public List> combinationSum(int[] candidates, int target) { + if (candidates == null || candidates.length == 0) return Collections.emptyList(); + List> result = new ArrayList<>(); + + combinationSumUtil(candidates, target, result, new ArrayList<>(), 0); + + return result; + + } + + public void combinationSumUtil(int[] candidates, int target, List> result, List tempList, int start) { + if (target < 0) return; + if (target == 0) { + result.add(new ArrayList<>(tempList)); + + } + + for (int i = start; i < candidates.length; i++) { + if (target - candidates[i] < 0) break; + tempList.add(candidates[i]); + // The same repeated number may be chosen from candidates unlimited number of times. + // that's the reason of using i not i+1 + combinationSumUtil(candidates, target - candidates[i], result, tempList, i); + + tempList.remove(tempList.size() - 1); + } + } + + public List> combinationSum2(int[] candidates, int target) { + if (candidates == null || candidates.length == 0) return Collections.emptyList(); + List> result = new ArrayList<>(); + Arrays.sort(candidates); + combinationSumUtil2(candidates, target, result, new ArrayList<>(), 0); + + return result; + } + + public void combinationSumUtil2(int[] candidates, int target, List> result, List tempList, int start) { + if (target < 0) return; + + if (target == 0) { + result.add(new ArrayList<>(tempList)); + return; + } + + for (int i = start; i < candidates.length; i++) { +// when we should skip a number? not just it's the same as previous number, +// but also when it's previous number haven't been added! +// i > cur means cand[i - 1] is not added +// to the path (you should know why if you understand the algorithm), +// so if cand[i] == cand[i-1], then we shouldn't add cand[i]. +// This tricky is very smart. + + /** + * i>start && candidates[i]==candidates[i-1] : + * when input is like 1,1,2,4,7,8 and target is 9 + * the first iteration would run for all combinations starts with 1 ([1,1,7], [1,8]) + * the next number i=1 also starts with one which will run again for all combinations + * candidates[i]==candidates[i-1] this will eliminate the duplicate combinations(another [1,8]) + */ + if (i > start && candidates[i] == candidates[i - 1]) continue; + if (target - candidates[i] < 0) break; + tempList.add(candidates[i]); + combinationSumUtil2(candidates, target - candidates[i], result, tempList, i + 1); + tempList.remove(tempList.size() - 1); + } + } + + public int combinationSum4BottomUp(int[] nums, int target) { + int[] dp = new int[target + 1]; + dp[0] = 1; + + // the difference with coin change is in coin change we see unique combination + // so outer loop is coins i.e we start with coins, but here we care about + // the number of permutations, so we start from target + // if you treat the nums as coins then we have unlimited number of coins + // that's the reason for the change in order + // For e.g. the classic coin change will exclude {2,1,1} after considering {1,1,2} as a solution. + /* + Coin change-2 + for (int coin : coins) { + for (int i = 1; i <= amount; i++) { + if (i >= coin) { + dp[i] += dp[i - coin]; + } + */ + for (int i = 1; i <= target; i++) { + for (int num : nums) { + if (i - num >= 0) dp[i] += dp[i - num]; + } + } + //System.out.println(Arrays.toString(dp)); + return dp[target]; + } + + Integer[] cache; + + public int combinationSum4(int[] nums, int target) { + return recursionHelper(nums, target, 0, new Integer[target + 1]); + } + + private int recursionHelper(int[] nums, int target, int pos, Integer[] cache) { + if (pos == nums.length || target <= 0) { + return (target == 0) ? 1 : 0; + } + + if (cache[target] != null) { + return cache[target]; + } + + int take = recursionHelper(nums, target - nums[pos], 0, cache); + int skip = recursionHelper(nums, target, pos + 1, cache); + + return cache[target] = take + skip; + } +} diff --git a/src/main/java/combinationsandpermutations/FactorCombinations.java b/src/main/java/combinationsandpermutations/FactorCombinations.java new file mode 100644 index 0000000..489681e --- /dev/null +++ b/src/main/java/combinationsandpermutations/FactorCombinations.java @@ -0,0 +1,33 @@ +package combinationsandpermutations; + +import java.util.ArrayList; +import java.util.List; + +/** + * https://leetcode.com/problems/factor-combinations + */ +public class FactorCombinations { + + public List> getFactors(int n) { + List> result = new ArrayList<>(); + helper(result, new ArrayList(), n, 2); + return result; + } + + public void helper(List> result, List item, int n, int start) { + if (n <= 1) { + if (item.size() > 1) { + result.add(new ArrayList<>(item)); + } + return; + } + + for (int i = start; i <= n; ++i) { + if (n % i == 0) { + item.add(i); + helper(result, item, n / i, i); + item.remove(item.size() - 1); + } + } + } +} diff --git a/src/main/java/combinationsandpermutations/Permutations.java b/src/main/java/combinationsandpermutations/Permutations.java new file mode 100644 index 0000000..ead11cd --- /dev/null +++ b/src/main/java/combinationsandpermutations/Permutations.java @@ -0,0 +1,73 @@ +package combinationsandpermutations; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * https://leetcode.com/problems/permutations/ + */ +public class Permutations { + + public List> permute(int[] nums) { + if (nums == null || nums.length == 0) return Collections.emptyList(); + List> result = new ArrayList<>(); + permuteUtils(nums, result, new ArrayList<>(), new HashSet<>()); + + return result; + } + + public void permuteUtils(int[] nums, List> result, List tempList, Set set) { + + if (tempList.size() == nums.length) { + result.add(new ArrayList<>(tempList)); + return; + } + + for (int num : nums) { +// 1 + (permutations of 2, 3, 4) +// +// 2 + (permutations of 1, 3, 4) +// +// 3 + (permutations of 1, 2, 4) +// +// 4 + (permutations of 1, 2, 3) + if (set.contains(num)) continue; + tempList.add(num); + set.add(num); + permuteUtils(nums, result, tempList, set); + set.remove(tempList.get(tempList.size() - 1)); + tempList.remove(tempList.size() - 1); + } + } + + public List> permuteUnique(int[] nums) { + List> list = new ArrayList<>(); + Arrays.sort(nums); + backtrack(list, new ArrayList<>(), nums, new boolean[nums.length]); + return list; + } + + private void backtrack(List> list, List tempList, int[] nums, boolean[] used) { + if (tempList.size() == nums.length) { + list.add(new ArrayList<>(tempList)); + } else { + for (int i = 0; i < nums.length; i++) { + //[1, 1, 2][1, 2, 1][2, 1, 1] + //[1, 2, 3][1, 3, 2][2, 1, 3][2, 3, 1][3, 1, 2][3, 2, 1] + if (used[i] || i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) continue; + used[i] = true; + tempList.add(nums[i]); + backtrack(list, tempList, nums, used); + used[i] = false; + tempList.remove(tempList.size() - 1); + } + } + } + +} + + diff --git a/src/main/java/combinationsandpermutations/SplitUniqueSubstring.java b/src/main/java/combinationsandpermutations/SplitUniqueSubstring.java new file mode 100644 index 0000000..8a5aa75 --- /dev/null +++ b/src/main/java/combinationsandpermutations/SplitUniqueSubstring.java @@ -0,0 +1,42 @@ +package combinationsandpermutations; + +import java.util.HashSet; +import java.util.Set; + +public class SplitUniqueSubstring { + public int maxUniqueSplit(String s) { + int[] result= new int[]{0}; + recursionHelper(s, 0, new HashSet<>(), result); + return result[0]; + } + + /** + * if(start == s.length()) { + * max = Math.max(max, h.size()); + * } + * String res = ""; + * + * for(int i = start;i < s.length();i++) { + * res += s.charAt(i); + * if(h.contains(res)) continue; + * h.add(res); + * backtrack(s, i+1, h); + * h.remove(res); + * } + * } + */ + public void recursionHelper(String s, int index, Set cache, int[] result){ + if(index==s.length()) { + result[0]=Math.max(result[0],cache.size()); + return; + } + + for(int i= index;i> subsets(int[] nums) { + if(nums==null || nums.length==0) return Collections.emptyList(); + + List> result= new ArrayList<>(); + subSetGenerationUtil(nums, result, new ArrayList<>(), 0); + return result; + } + + public void subSetGenerationUtil(int[] nums, List> result, List tempList, int start){ + + result.add(new ArrayList<>(tempList)); + + for(int i=start;i> subsetsWithDup(int[] nums) { + if(nums==null || nums.length==0) return Collections.emptyList(); + + List> result= new ArrayList<>(); + Arrays.sort(nums); + //subsetsWithDupUtil(nums, result, new ArrayList<>(), 0); + dfs(nums,0,result, new ArrayList<>()); + return result; + } + + public void subsetsWithDupUtil(int[] nums, List> result, List tempList, int start){ + result.add(new ArrayList<>(tempList)); + + for(int i=start; istart && nums[i]==nums[i-1]) continue; + tempList.add(nums[i]); + subsetsWithDupUtil(nums, result, tempList, i+1); + tempList.remove(tempList.size()-1); + } + } + + private void dfs(int[] nums, int index, List> res, List curr){ + res.add(new ArrayList<>(curr)); + if(index == nums.length){ + return; + } + Set visited = new HashSet(); + for(int i = index; i < nums.length; i++){ + if(visited.add(nums[i])){ + curr.add(nums[i]); + dfs(nums, i + 1, res, curr); + curr.remove(curr.size() - 1); + } + } + } + + public static void main(String[] args) { + new SubSets().subsetsWithDup(new int[]{1,1,2,4}); + } +} + diff --git a/src/main/java/dynamicProgramming/AssemblyLineScheduling.java b/src/main/java/dynamicProgramming/AssemblyLineScheduling.java new file mode 100644 index 0000000..fe61d10 --- /dev/null +++ b/src/main/java/dynamicProgramming/AssemblyLineScheduling.java @@ -0,0 +1,47 @@ +package dynamicProgramming; + +class AssemblyLineScheduling +{ + static int NUM_STATION = 4; + + static int carAssembly(int a[][], int t[][], int e[], int x[]) + { + int T1[]= new int [NUM_STATION]; + int T2[] =new int[NUM_STATION] ; + int i; + + // time taken to leave first station in line 1 + T1[0] = e[0] + a[0][0]; + + // time taken to leave first station in line 2 + T2[0] = e[1] + a[1][0]; + + // Fill tables T1[] and T2[] using + // the above given recursive relations + for (i = 1; i < NUM_STATION; ++i) + { + T1[i] = Math.min(T1[i - 1] + a[0][i], + T2[i - 1] + t[1][i] + a[0][i]); + T2[i] = Math.min(T2[i - 1] + a[1][i], + T1[i - 1] + t[0][i] + a[1][i]); + } + + // Consider exit times and retutn minimum + return Math.min(T1[NUM_STATION-1] + x[0], + T2[NUM_STATION-1] + x[1]); + } + + + // Driver code + public static void main (String[] args) + { + int a[][] = {{4, 5, 3, 2}, + {2, 10, 1, 4}}; + int t[][] = {{0, 7, 4, 5}, + {0, 9, 2, 8}}; + int e[] = {10, 12}, x[] = {18, 7}; + + System.out.println(carAssembly(a, t, e, x)); + + } +} \ No newline at end of file diff --git a/src/main/java/dynamicProgramming/BooleanParenthesization.java b/src/main/java/dynamicProgramming/BooleanParenthesization.java new file mode 100644 index 0000000..af5ebea --- /dev/null +++ b/src/main/java/dynamicProgramming/BooleanParenthesization.java @@ -0,0 +1,63 @@ +package dynamicProgramming; + +class BooleanParenthesization { + static int countParenth(char[] symb, char[] oper, int n) { + int[][] F = new int[n][n]; + int[][] T = new int[n][n]; + + // Fill diagonal entries first + // All diagonal entries in T[i][i] + // are 1 if symbol[i] is T (true). + // Similarly, all F[i][i] entries + // are 1 if symbol[i] is F (False) + for (int i = 0; i < n; i++) { + F[i][i] = (symb[i] == 'F') ? 1 : 0; + T[i][i] = (symb[i] == 'T') ? 1 : 0; + } + + // Now fill T[i][i+1], T[i][i+2], + // T[i][i+3]... in order And F[i][i+1], + // F[i][i+2], F[i][i+3]... in order + for (int gap = 1; gap < n; ++gap) { + for (int i = 0, j = gap; j < n; ++i, ++j) { + T[i][j] = F[i][j] = 0; + for (int g = 0; g < gap; g++) { + // Find place of parenthesization + // using current value of gap + int k = i + g; + + // Store Total[i][k] and Total[k+1][j] + int tik = T[i][k] + F[i][k]; + int tkj = T[k + 1][j] + F[k + 1][j]; + + // Follow the recursive formulas + // according to the current operator + if (oper[k] == '&') { + T[i][j] += T[i][k] * T[k + 1][j]; + F[i][j] += (tik * tkj - T[i][k] * T[k + 1][j]); + } + if (oper[k] == '|') { + F[i][j] += F[i][k] * F[k + 1][j]; + T[i][j] += (tik * tkj - F[i][k] * F[k + 1][j]); + } + if (oper[k] == '^') { + T[i][j] += F[i][k] * T[k + 1][j] + T[i][k] * F[k + 1][j]; + F[i][j] += T[i][k] * T[k + 1][j] + F[i][k] * F[k + 1][j]; + } + } + } + } + return T[0][n - 1]; + } + + public static void main(String[] args) { + char symbols[] = "TTFT".toCharArray(); + char operators[] = "|&^".toCharArray(); + int n = symbols.length; + + // There are 4 ways + // ((T|T)&(F^T)), (T|(T&(F^T))), + // (((T|T)&F)^T) and (T|((T&F)^T)) + System.out.println(countParenth(symbols, operators, n)); + } +} \ No newline at end of file diff --git a/src/main/java/dynamicProgramming/BoxStacking.java b/src/main/java/dynamicProgramming/BoxStacking.java new file mode 100644 index 0000000..24f8b7e --- /dev/null +++ b/src/main/java/dynamicProgramming/BoxStacking.java @@ -0,0 +1,155 @@ +package dynamicProgramming; + +import java.util.Arrays; + +/** + * Date 05/09/2015 + * + * @author tusroy + * + * 1) Create all rotations of boxes such that length is always greater + * or equal to width 2) Sort boxes by base area in non increasing order + * (length * width). This is because box with more area will never ever + * go on top of box with less area. 3) Take T[] and result[] array of + * same size as total boxes after all rotations are done 4) Apply + * longest increasing subsequence type of algorithm to get max height. + * + * + * References + * http://www.geeksforgeeks.org/dynamic-programming-set-21-box-stacking-problem/ + * http://people.cs.clemson.edu/~bcdean/dp_practice/ + */ +public class BoxStacking { + //We can use multiple instances of boxes. What it means is, we can have two different rotations of a box as part of our maximum height stack. + + /** + * You can rearrange any cuboid's dimensions by rotating it to put it on another cuboid. + * So for each cuboid, we sort its length in three dimension. + *

+ * You can place cuboid i on cuboid j, + * we have + * width[i] <= width[j] and length[i] <= length[j] and height[i] <= height[j]. + *

+ * This condition will hold, after we sort each cuboid length, + * that is, + * small[i] <= small[j] and mid[i] <= mid[j] and big[i] <= big[j]. + */ + public int maxHeight(int[][] cuboids) { + for (int[] cube : cuboids) Arrays.sort(cube); + Arrays.sort(cuboids, (a, b) -> (a[0] + a[1] + a[2]) - (b[0] + b[1] + b[2])); + int N = cuboids.length, res = 0; + int[] dp = new int[N]; + for (int i = 0; i < N; i++) { + dp[i] = cuboids[i][2]; + res = Math.max(res, dp[i]); + } + for (int i = 1; i < N; i++) + for (int j = 0; j < i; j++) + if (cuboids[j][0] <= cuboids[i][0] && + cuboids[j][1] <= cuboids[i][1] && + cuboids[j][2] <= cuboids[i][2]) { + dp[i] = Math.max(dp[i], dp[j] + cuboids[i][2]); + res = Math.max(res, dp[i]); + } + return res; + } + + public int maxHeight(Dimension[] input) { + Dimension[] allRotationInput = new Dimension[input.length * 3]; + createAllRotation(input, allRotationInput); + + Arrays.sort(allRotationInput); + + System.out.println(Arrays.toString(allRotationInput)); + + int[] T = new int[allRotationInput.length]; + int[] result = new int[allRotationInput.length]; + + for (int i = 0; i < T.length; i++) { + T[i] = allRotationInput[i].height; + result[i] = i; + } + + for (int i = 1; i < T.length; i++) { + for (int j = 0; j < i; j++) { + if (allRotationInput[i].length < allRotationInput[j].length + && allRotationInput[i].width < allRotationInput[j].width) { + if (T[j] + allRotationInput[i].height > T[i]) { + T[i] = T[j] + allRotationInput[i].height; + result[i] = j; + } + } + } + } + + int max = Integer.MIN_VALUE; + for (int j : T) { + if (j > max) { + max = j; + } + } + + return max; + } + + private void createAllRotation(Dimension[] input, Dimension[] allRotationInput) { + int index = 0; + for (int i = 0; i < input.length; i++) { + allRotationInput[index++] = Dimension.createDimension(input[i].height, input[i].length, input[i].width); + allRotationInput[index++] = Dimension.createDimension(input[i].length, input[i].height, input[i].width); + allRotationInput[index++] = Dimension.createDimension(input[i].width, input[i].length, input[i].height); + + } + } + + public static void main(String args[]) { + BoxStacking bs = new BoxStacking(); + Dimension[] input = {new Dimension(3, 2, 5), new Dimension(1, 2, 4)}; + int maxHeight = bs.maxHeight(input); + System.out.println("Max height is " + maxHeight); + assert 11 == maxHeight; + } + + + static class Dimension implements Comparable { + int height; + int length; + int width; + + Dimension(int height, int length, int width) { + this.height = height; + this.length = length; + this.width = width; + } + + Dimension() { + } + + static Dimension createDimension(int height, int side1, int side2) { + Dimension d = new Dimension(); + d.height = height; + if (side1 >= side2) { + d.length = side1; + d.width = side2; + } else { + d.length = side2; + d.width = side1; + } + return d; + } + + @Override + public int compareTo(Dimension d) { + if (this.length * this.width >= d.length * d.width) { + return 1; + } else { + return -1; + } + } + + @Override + public String toString() { + return "Dimension [height=" + height + ", length=" + length + ", width=" + width + "]"; + } + } +} \ No newline at end of file diff --git a/src/main/java/dynamicProgramming/CatalanNumberBinarySearchTree.java b/src/main/java/dynamicProgramming/CatalanNumberBinarySearchTree.java new file mode 100644 index 0000000..f439f1a --- /dev/null +++ b/src/main/java/dynamicProgramming/CatalanNumberBinarySearchTree.java @@ -0,0 +1,81 @@ +package dynamicProgramming; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * https://www.youtube.com/watch?v=0pTN0qzpt-Y + */ +public class CatalanNumberBinarySearchTree { + public int countTrees(int n) { + int[] T = new int[n + 1]; + T[0] = 1; + T[1] = 1; + for (int i = 2; i <= n; i++) { + for (int j = 0; j < i; j++) { + int i1 = T[j] * T[i - j - 1]; + System.out.println("T[" + j + "]" + "*" + "T[" + (i - j - 1) + "] :: " + i1); + T[i] += i1; + } + } + return T[n]; + } + + Integer[] cache = new Integer[20]; + + public int numTrees(int n) { + if (n < 0) return 0; + if (n == 0) return 1; + if (n == 1) return 1; + int result = 0; + if (cache[n] != null) return cache[n]; + for (int k = 1; k <= n; k++) { + result += numTrees(k - 1) * numTrees(n - k); + } + return cache[n] = result; + } + + /** + * Given a sequence 1…n, to construct a Binary Search Tree (BST) out of the sequence, + * we could enumerate each number i in the sequence, and use the number as the root, + * naturally, the subsequence 1…(i-1) on its left side would lay on the left branch of the root, + * and similarly the right subsequence (i+1)…n lay on the right branch of the root. + * We then can construct the subtree from the subsequence recursively. + * Through the above approach, we could ensure that the BST that we construct are all unique, + * since they have unique roots. + * + * G(n) = F(1, n) + F(2, n) + ... + F(n, n). + * + * For example, F(3, 7): the number of unique BST tree with number 3 as its root. + * To construct an unique BST out of the entire sequence [1, 2, 3, 4, 5, 6, 7] with 3 as the root, + * which is to say, we need to construct an unique BST out of its left subsequence [1, 2] + * and another BST out of the right subsequence [4, 5, 6, 7], and then combine them together (i.e. cartesian product). + * The tricky part is that we could consider the number of unique BST out of sequence [1,2] as G(2), + * and the number of of unique BST out of sequence [4, 5, 6, 7] as G(4). Therefore, F(3,7) = G(2) * G(4). + * + * F(i, n) = G(i-1) * G(n-i) 1 <= i <= n + * @param n + * @return + */ + public int numTreesBottomUp(int n) { + if (n < 0) return 0; + int[] cache = new int[20]; + cache[0] = 1; + cache[1] = 1; + for (int i = 2; i <= n; i++) { + for (int j = 1; j <= i; j++) { + System.out.println(j - 1+" : "+(i - j)); + cache[i] += cache[j - 1] * cache[i - j]; + } + } + return cache[n]; + } + + public static void main(String[] args) { + CatalanNumberBinarySearchTree cnt = new CatalanNumberBinarySearchTree(); + System.out.println(); + System.out.println(cnt.numTreesBottomUp(5)); + } + +} diff --git a/src/main/java/dynamicProgramming/EggDropping.java b/src/main/java/dynamicProgramming/EggDropping.java new file mode 100644 index 0000000..8d2bb77 --- /dev/null +++ b/src/main/java/dynamicProgramming/EggDropping.java @@ -0,0 +1,59 @@ +package dynamicProgramming; + +/** + * http://www.geeksforgeeks.org/dynamic-programming-set-11-egg-dropping-puzzle/ + *

+ * https://www.youtube.com/watch?v=3hcaVyX00_4&t=258s + */ +public class EggDropping { + // calculate minimum tries to try to find the floor which breaks the egg + public int calculate(int eggs, int floors) { + + int[][] T = new int[eggs + 1][floors + 1]; + int c = 0; + // We need one trial for one floor + for (int i = 0; i <= floors; i++) { + T[1][i] = i; + } + + for (int e = 2; e <= eggs; e++) { + for (int f = 1; f <= floors; f++) { + T[e][f] = Integer.MAX_VALUE; + for (int k = 1; k <= f; k++) { // for loop to go from 0-f floor + // if egg breaks then egg-1 and floor -1 ==> T[e - 1][k - 1] + // else no change in egg count and remaining floors which is f-k ==> T[e][f - k] + // k means which floor we are in -- > first floor , second floor + // 2 Eggs -> 3 Floors + // • 2 Eggs -> 1 Floor-> 1,0 ,, 2,2(remaining floors) + // • 2 Eggs -> 2nd Floor -> 1,1 ,, 2,1 (remaining floors) + // 2 Eggs -> 3rd Floor -> 1,2 ,, 2,0 (remaining floors) + System.out.println("e - " + e + " :: f - " + f + " :: k - " + k); + System.out.println("T[" + (e - 1) + "][" + (k - 1) + "],T[" + e + "][" + (f - k) + "]"); + c = 1 + Math.max(T[e - 1][k - 1], T[e][f - k]); + if (c < T[e][f]) { + T[e][f] = c; + } + } + } + } + return T[eggs][floors]; + } + + public static int superEggDrop(int K, int N) { + int[][] dp = new int[N + 1][K + 1]; + int m = 0; + while (dp[m][K] < N) { + ++m; + for (int k = 1; k <= K; ++k) + dp[m][k] = dp[m - 1][k - 1] + dp[m - 1][k] + 1; + } + return m; + } + + public static void main(String args[]) { + EggDropping ed = new EggDropping(); + // System.out.println(ed.calculate(2, 6)); + System.out.println(superEggDrop(2, 6)); + } + +} \ No newline at end of file diff --git a/src/main/java/dynamicProgramming/KnightDialer.java b/src/main/java/dynamicProgramming/KnightDialer.java new file mode 100644 index 0000000..d6e8332 --- /dev/null +++ b/src/main/java/dynamicProgramming/KnightDialer.java @@ -0,0 +1,73 @@ +package dynamicProgramming; + +import java.util.HashMap; +import java.util.Map; + +/** + * https://leetcode.com/problems/knight-dialer/ + */ +public class KnightDialer { + int[][] pos = new int[][]{{2, -1}, {2, 1}, {1, -2}, {1, 2}, {-1, 2}, {-1, -2}, {-2, -1}, {-2, 1}}; + int max = (int) Math.pow(10, 9) + 7; + Map cache = new HashMap<>(); + + public int knightDialer(int n) { + long s = 0; + //do n hops from every i, j index (the very requirement of the problem) + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 3; j++) { + s = (s + recursionHelper(n, i, j)) % max; + } + } + return (int) s; + } + + public int recursionHelper(int n, int i, int j) { + if (i < 0 || j < 0 || i >= 4 || j >= 3 || (i == 3 && j != 1)) return 0; + + if (1 == n) return 1; + String key = i + "" + j + "" + n; + if (cache.containsKey(key)) return cache.get(key); + + int res = 0; + for (int[] p : pos) { + res += recursionHelper(n - 1, i + p[0], j + p[1]); + res %= max; + } + cache.put(key, res); + return cache.get(key); + } + + public static int knightDialerBottomUp(int n) { + int MOD = 1000000007; + int[][] paths = {{4, 6}, {6, 8}, {7, 9}, {4, 8}, {0, 3, 9}, {}, {0, 1, 7}, {2, 6}, {1, 3}, {2, 4}}; // Previous moves of knight-> For instance, if a knight is at 0, it reached from either 4 or 6. Similarly if it is at 1, it is reached from 7 or 9 & so on + int[][] dp = new int[n + 1][10]; // rows -> no of steps taken to reach row i cols-> no of digits + for (int j = 0; j < 10; j++) + dp[1][j] = 1; //populate the base case for n =1 + + for (int i = 2; i <=n ; i++) { // no of steps taken by knight to reach i + for (int j = 0; j < 10; j++) { // no of digits + for (int p : paths[j]) { // Previous move of knight in order to reach digit j + dp[i][j] += dp[i - 1][p]; // cumulatively add from the previous knight move. For instance., F(2, 0) -> F(1,4) + F(1,6) F(2,6) -> F(1,0) + F(1,1) + F(1,7) + } + dp[i][j] %= MOD; + } + } + + for (int i = 0; i <=n ; i++) { // no of steps taken by knight to reach i + for (int j = 0; j < 10; j++) { + System.out.print(dp[i][j] +" "); + } + System.out.println(); + } + + double sum = 0d; + for (int j = 0; j < 10; j++) + sum += dp[n][j]; + return (int) (sum % MOD); + } + + public static void main(String[] args) { + System.out.println(knightDialerBottomUp(3)); + } +} diff --git a/src/main/java/dynamicProgramming/LastStoneWeight.java b/src/main/java/dynamicProgramming/LastStoneWeight.java new file mode 100644 index 0000000..48bdf55 --- /dev/null +++ b/src/main/java/dynamicProgramming/LastStoneWeight.java @@ -0,0 +1,111 @@ +package dynamicProgramming; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * https://leetcode.com/problems/last-stone-weight-ii/ + *

+ * https://www.youtube.com/watch?v=TaZxJt4-FHk + *

+ * For example, we take array [2,3,3,2,5]. We partition it into two sets [2,3,2] and [3,5], with sum 7 and 8 respectively. + * If we have collision with numbers from set 1 with those of set 2, only 7~8=1 will remain. + * so this becomes subset sum with min difference + *

+ * So the problem asks us to accumulate stones such the result is minimum, + * but it really is this problem in disguise, divide the array into two subsets such that their sum is minimum. Why? + * Suppose given array is [1,5,3,2] and you go about solving the problem as given you would take the following steps right? + *

+ * 5 - 3 = 2 + * 2 - 2 = 0 (or) 2 - (5 - 3) + * 1 - 0 = 1 (or) 1 - (2 - (5 - 3)) => sum{1,5} - sum{2,3} + * So do you see why we want the minimum difference between two subsets?(this is just intuition not exact mathematical proof) + */ +public class LastStoneWeight { + static Map cache = new HashMap<>(); + + public static int lastStoneWeightIIRecur(int[] stones) { + + int ans = recursionHelper(stones, 0, 0, 0); + cache.forEach((key, value) -> System.out.print(key + " :: " + value + "")); + return ans; + } + + public static int recursionHelper(int[] stones, int index, int sum1, int sum2) { + if (index == stones.length) { + return Math.abs(sum1 - sum2); + } + String uniqueKey = index+"-"+sum1 + "-" + sum2; + + if (cache.containsKey(uniqueKey)) { + return cache.get(uniqueKey); + } + int leftPath = recursionHelper(stones, index + 1, sum1 + stones[index], sum2); + int rightPath = recursionHelper(stones, index + 1, sum1, sum2 + stones[index]); + + cache.put(uniqueKey, Math.min(leftPath, rightPath)); + return cache.get(uniqueKey); + + } + + public static void main(String[] args) { + lastStoneWeightII2D(new int[]{31,26,33,21,40}); + } + + public static int lastStoneWeightII(int[] stones) { + + int total = 0; + for (int stone : stones) total += stone; + int S2 = 0; + int target = total/2; + boolean[] dp = new boolean[target + 1]; // use only 1D array to store the status + dp[0] = true; + for (int s : stones) { + boolean[] cur = dp.clone(); + for (int i = s; i <= target; i++) { // wont affect the value that is smaller than current stone's weight + if (dp[i - s]) { // checks if the subset sum is there if i=9 and s=7 at 9th pos true entry + // will be present because the 9-7=2 is there, like wise it checks for all combinations + cur[i] = true; + S2 = Math.max(S2, i); + } + } + dp = cur; + } + return total - 2 * S2; + } + public static int lastStoneWeightII2D(int[] stones) { + int n = stones.length, sum = 0; + for (int s : stones) sum += s; + int target = sum/2; + + int[][] dp = new int[n + 1][target + 1]; + for (int i = 1; i <= n; i++) { + int currStone = stones[i-1]; + for (int j = 0; j <= target; j++) { + if (j >= currStone) { + dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - currStone] + currStone); + } else { + dp[i][j] = dp[i - 1][j]; + } + } + } + + System.out.println("Sum is :: "+sum); + for(int []d:dp){ + System.out.println(Arrays.toString(d)); + } + System.out.println(sum +" - "+ 2 * dp[n][target]); + + /** dp[n][target] -> this is closest we can partition to sum/2 + * //notice that in general I can say that + * //answer = S1-S2 + * //where S1 is sum of some numbers and S2 is sum of rest of numbers + * //also note that S1+S2 = SUM (sum of all numbers) + * //S1 >= S2 because negative answer is not possible + * //now we have to minimise answer + * //answer = (SUM-S2)-S2 = > SUM-2*S2 (Just substituting S1 by SUM-S2) + */ + return sum - 2 * dp[n][target]; + } +} diff --git a/src/main/java/dynamicProgramming/MakeArrayStrictlyIncreasing.java b/src/main/java/dynamicProgramming/MakeArrayStrictlyIncreasing.java new file mode 100644 index 0000000..03a7769 --- /dev/null +++ b/src/main/java/dynamicProgramming/MakeArrayStrictlyIncreasing.java @@ -0,0 +1,32 @@ +package dynamicProgramming; + +/** + * https://leetcode.com/problems/remove-one-element-to-make-the-array-strictly-increasing + */ +public class MakeArrayStrictlyIncreasing { + public boolean canBeIncreasing(int[] nums) { + int numberOfDeletionsNeededToMakeStrictlyIncreasing = 0; + for (int i = 1; i < nums.length; i++) { + if (nums[i - 1] >= nums[i]) { + + int iMinusTwoValue = i - 2 < 0 ? Integer.MIN_VALUE : nums[i - 2]; + int iPlusOneValue = i + 1 == nums.length ? Integer.MAX_VALUE : nums[i + 1]; + + //[1,2,10,5,7] + //[2,3,1,2] -- false + //[1,1,1] -- false + //[100,21,100] + //[1,2,3] + //[105,924,32,968] + if ((nums[i] > iMinusTwoValue && nums[i] < iPlusOneValue) || + (nums[i - 1] > iMinusTwoValue && nums[i - 1] < iPlusOneValue)) { + ++numberOfDeletionsNeededToMakeStrictlyIncreasing; + } else { + return false; // found an unfixable non-increase + } + + } + } + return numberOfDeletionsNeededToMakeStrictlyIncreasing <= 1; + } +} diff --git a/src/main/java/dynamicProgramming/MaxGoldAMinerCanGet.java b/src/main/java/dynamicProgramming/MaxGoldAMinerCanGet.java new file mode 100644 index 0000000..9948d0c --- /dev/null +++ b/src/main/java/dynamicProgramming/MaxGoldAMinerCanGet.java @@ -0,0 +1,48 @@ +package dynamicProgramming; + +import java.util.Arrays; + +/** + * Freshworks interview question: + * Miner can start at any of the i,j in first column + * miner can move in right, right upper diagonal and right lower diagonal + */ +public class MaxGoldAMinerCanGet { + + public static int getMaxGoldMinerCanGet(int[][] matrix) { + if (matrix.length == 0) return 0; + + int m = matrix.length; + int n = matrix[0].length; + int result = -1; + for (int j = 1; j < n; j++) { + for (int i = 0; i < m; i++) { + int left = matrix[i][j - 1]; + int leftUpperDiagonal = i - 1 >= 0 ? matrix[i - 1][j - 1] : 0; + int leftLowerDiagonal = i + 1 < m ? matrix[i + 1][j - 1] : 0; + + matrix[i][j] = Math.max(left, Math.max(leftUpperDiagonal, leftLowerDiagonal)) + matrix[i][j]; + result = Math.max(result, matrix[i][j]); + } + } + + for (int[] ints : matrix) { + System.out.println(Arrays.toString(ints)); + } + + return result; + + } + + public static void main(String[] args) { + System.out.println(getMaxGoldMinerCanGet(new int[][]{{1, 3, 1, 5}, + {2, 2, 4, 1}, + {5, 0, 2, 3}, + {0, 6, 1, 2}} + )); + System.out.println(getMaxGoldMinerCanGet(new int[][]{{1, 3, 3}, + {2, 1, 4}, + {0, 6, 4}} + )); + } +} diff --git a/src/main/java/dynamicProgramming/MaxSumForNonAdjacentElements.java b/src/main/java/dynamicProgramming/MaxSumForNonAdjacentElements.java new file mode 100644 index 0000000..684486b --- /dev/null +++ b/src/main/java/dynamicProgramming/MaxSumForNonAdjacentElements.java @@ -0,0 +1,27 @@ +package dynamicProgramming; + +/** + * @author Tushar Roy + * + * https://www.geeksforgeeks.org/maximum-sum-such-that-no-two-elements-are-adjacent/ + */ +public class MaxSumForNonAdjacentElements { + + public int maxSum(int arr[]) { + int excl = 0; + int incl = arr[0]; + for (int i = 1; i < arr.length; i++) { + int oldResult = incl; + incl = Math.max(excl + arr[i], incl); + excl = oldResult; + } + return incl; + } + + public static void main(String args[]) { + MaxSumForNonAdjacentElements msn = new MaxSumForNonAdjacentElements(); + int arr[] = { 4, 1, 1, 4, 2, 1 }; + System.out.println(msn.maxSum(arr)); + + } +} \ No newline at end of file diff --git a/src/main/java/dynamicProgramming/MaximumRectangle.java b/src/main/java/dynamicProgramming/MaximumRectangle.java new file mode 100644 index 0000000..b866d13 --- /dev/null +++ b/src/main/java/dynamicProgramming/MaximumRectangle.java @@ -0,0 +1,99 @@ +package dynamicProgramming; + +import practiceproblems.stack.MaxHistogram; + +import java.util.Arrays; + +/** + * At every row the height and area of rectangle varies, if the last row(base) is having a zero value + * then that portion have to be avoided, this is the reason we take maxHistogram after every row + *

+ * matrix + * 0 0 0 1 0 0 0 + * 0 0 1 1 1 0 0 + * 0 1 1 1 1 1 0 + *

+ * height + * 0 0 0 1 0 0 0 + * 0 0 1 2 1 0 0 + * 0 1 2 3 2 1 0 + */ +public class MaximumRectangle { + + public static int maximalRectangle(char[][] matrix) { + if (matrix.length == 0) return 0; + int[] row = new int[matrix[0].length]; + int result = 0; + for (char[] arr : matrix) { + int i = 0; + // for each cell with value=1, we look upward (north), the number of continuous '1' is the height of cell + //First initiate the height array as 1 1 0 1 0 1, which is just a copy of the first row. Then we can easily calculate the max area is 2. + //Then update the array. We scan the second row, when the matrix[1][i] is 0, set the height[i] to 0 + //else height[i] += 1, which means the height has increased by 1. So the height array again becomes 0 2 0 0 1 2. + // The max area now is also 2. + for (char c : arr) { + if (c - '0' > 0) { + row[i] += c - '0'; + } else { + row[i] = 0; + } + + i++; + } + /** The result row for every iteration + * [1, 0, 1, 0, 0] + * [2, 0, 2, 1, 1] + * [3, 1, 3, 2, 2] + * [4, 0, 0, 3, 0] + */ + System.out.println(Arrays.toString(row)); + result = Math.max(result, MaxHistogram.largestRectangleArea(row)); + } + + return result; + } + + public static void main(String[] args) { + maximalRectangle(new char[][]{{'1', '0', '1', '0', '0'}, + {'1', '0', '1', '1', '1'}, + {'1', '1', '1', '1', '1'}, + {'1', '0', '0', '1', '0'}}); + } + + /** + * https://www.youtube.com/watch?v=-FgseNO-6Gk + */ + public int maximalRectangleBruteForce(char[][] matrix) { + int rowLength = matrix.length; + if (rowLength == 0) return 0; + int colLength = matrix[0].length; + if (colLength == 0) return 0; + + int maxA = Integer.MIN_VALUE; + + for (int row = 0; row < rowLength; row++) { + for (int col = 0; col < colLength; col++) { + if (matrix[row][col] == '0') continue; + // consider the rectangle whose upper left corner is at [r, c]: + + int rightMostCol = colLength - 1;// the right most column that we should check, initially it's w-1 + + // iterate from i,j till row and col end + for (int r1 = row; r1 < rowLength; r1++) { + for (int c1 = col; c1 <= rightMostCol; c1++) { + + if (matrix[r1][c1] == '0') { + rightMostCol = c1 - 1; // update the rightMostCol when we encounter '0', no use in proceeding after + break; // go to next row + } + // r1 - row + 1, c1 - col + 1 are rectangle size relative to i,j where the inner loop started + maxA = Math.max(maxA, (r1 - row + 1) * (c1 - col + 1)); // + } + } + } + } + + return Math.max(maxA, 0); + } + +} diff --git a/src/main/java/dynamicProgramming/MinCoinChange.java b/src/main/java/dynamicProgramming/MinCoinChange.java new file mode 100644 index 0000000..dfedffb --- /dev/null +++ b/src/main/java/dynamicProgramming/MinCoinChange.java @@ -0,0 +1,50 @@ +package dynamicProgramming; + +import java.util.Arrays; + +/** + * https://leetcode.com/problems/coin-change-2/ + */ +public class MinCoinChange { + + Integer[] cache; + + public int coinChange(int[] coins, int amount) { + cache = new Integer[amount + 1]; + return recursionHelper(amount, coins); + } + + public int recursionHelper(int amount, int[] coins) { + if (amount == 0) return 0; + if (amount < 0) return -1; + + if (cache[amount] != null) return cache[amount]; + + int minCount = Integer.MAX_VALUE; + + for (int coin : coins) { + int count = recursionHelper(amount - coin, coins); + if (count == -1) continue; + minCount = Math.min(minCount, count + 1); + } + + return cache[amount] = minCount == Integer.MAX_VALUE ? -1 : minCount; + + } + + + public int coinChangeBottomUp(int[] coins, int amount) { + Integer[] cache = new Integer[amount + 1]; + Arrays.fill(cache, amount + 1); + cache[0] = 0; + for (int coin : coins) { + for (int i = 1; i <= amount; i++) { + if (i - coin >= 0) { + cache[i] = Math.min(cache[i], cache[i - coin] + 1); + } + } + } + + return cache[amount] == amount + 1 ? -1 : cache[amount]; + } +} diff --git a/src/main/java/dynamicProgramming/MinCostTickets.java b/src/main/java/dynamicProgramming/MinCostTickets.java new file mode 100644 index 0000000..0d7a584 --- /dev/null +++ b/src/main/java/dynamicProgramming/MinCostTickets.java @@ -0,0 +1,67 @@ +package dynamicProgramming; + +public class MinCostTickets { + + //1,4,6,7,8,20 + //2,7,50 + public int mincostTickets(int[] days, int[] cost) { + int lastDayTravel = days[days.length - 1]; + + int[] daysDp = new int[lastDayTravel + 1]; + boolean[] travelDays = new boolean[lastDayTravel + 1]; + + for (int day : days) { + travelDays[day] = true; + } + + for (int i = 1; i <= lastDayTravel; i++) { + + if (!travelDays[i]) { + daysDp[i] = daysDp[i - 1]; + } else { + int oneDayPass = daysDp[i - 1] + cost[0]; + int sevenDaysPass = daysDp[Math.max(0, i - 7)] + cost[1]; + int thirtyDayPass = daysDp[Math.max(0, i - 30)] + cost[2]; + + daysDp[i] = Math.min(oneDayPass, Math.min(sevenDaysPass, thirtyDayPass)); + } + } + + return daysDp[lastDayTravel]; + } + + + private int recursionUtil(int totalDaysOfTravel, boolean[] willTravel, int[] costs, Integer[] dp) { + if (totalDaysOfTravel <= 0) return 0; + + if (dp[totalDaysOfTravel] != null) return dp[totalDaysOfTravel]; + + int daily; + if (willTravel[totalDaysOfTravel]) { + daily = recursionUtil(totalDaysOfTravel - 1, willTravel, costs, dp) + costs[0];// Corresponding to daily package + } else { + daily = recursionUtil(totalDaysOfTravel - 1, willTravel, costs, dp);// This is the case when we cant take daily package but still we need to make recursive call to change the state + } + + int weekly = recursionUtil(totalDaysOfTravel - 7, willTravel, costs, dp) + costs[1];// Corresponding to weekly package + int monthly = recursionUtil(totalDaysOfTravel - 30, willTravel, costs, dp) + costs[2];// Corresponding to monthly package + dp[totalDaysOfTravel] = Math.min(daily, Math.min(weekly, monthly)); + return dp[totalDaysOfTravel]; + + } + + // Time Complexity-: O(N) Space Complexity -: O(366) + public int mincostTicketsRecur(int[] days, int[] costs) { + int totalDaysOfTravel = days[days.length - 1]; + boolean[] willTravel = new boolean[totalDaysOfTravel+1]; + for (int day : days) { + willTravel[day] = true; + } + + Integer[] dp = new Integer[totalDaysOfTravel+1]; + recursionUtil(totalDaysOfTravel, willTravel, costs, dp); + return dp[totalDaysOfTravel]; + } + + +} diff --git a/src/main/java/dynamicProgramming/MinSwapsToMakeArrayIncreasing.java b/src/main/java/dynamicProgramming/MinSwapsToMakeArrayIncreasing.java new file mode 100644 index 0000000..85061bc --- /dev/null +++ b/src/main/java/dynamicProgramming/MinSwapsToMakeArrayIncreasing.java @@ -0,0 +1,113 @@ +package dynamicProgramming; + +/** + * https://leetcode.com/problems/minimum-swaps-to-make-sequences-increasing/ + */ +public class MinSwapsToMakeArrayIncreasing { + Integer[][] dp; + final int MAX = 10_000; + + /** + * There are two states possible, + *

+ * Don't swap elements at the current index + * Swap elements at the current index + *

+ * We just have to find out which one gives the minimum number of swaps for the rest of the array. + * That is, we will compute answer for both the states. + * The answer for the current state is dependent on the relation between the element at the current index + * and the previous index. + *

+ * If they are already in increasing order, t + * hen the state for the current index is applied to the previous index + * (that is, no swap remains no swap, swap remains swap). + * Else, the state for the current index is reversed for the previous index. + * But, what if swap and no swap both achieve the increasing order? In this case, + * we take the minimum of both states from the previous index. + * + * @param A + * @param B + * @return + */ + public int minSwapTopDown(int[] A, int[] B) { + dp = new Integer[A.length][2]; + return minSwapHelper(A, B, 0, -1, -1, 0); + } + + private int minSwapHelper(int[] A, int[] B, int i, int prevA, int prevB, int swapped) { + + if (i == A.length) return 0; + if (dp[i][swapped] != null) return dp[i][swapped]; + + int minSwaps = MAX; + + if (A[i] > prevA && B[i] > prevB) { // with-no-swap + minSwaps = minSwapHelper(A, B, i + 1, A[i], B[i], 0); + } + + if (B[i] > prevA && A[i] > prevB) { // with-swap + minSwaps = Math.min(minSwaps, minSwapHelper(A, B, i + 1, B[i], A[i], 1) + 1); + } + + dp[i][swapped] = minSwaps; + return minSwaps; + } + + /** + * https://www.youtube.com/watch?v=mLTF2UXkd2o + * @return + */ + public static int minSwap(int[] A, int[] B) { + + int[] noSwap = new int[A.length]; + int[] swap = new int[A.length]; + swap[0] = 1; + for (int i = 1; i < A.length; ++i) { + + /** // This is max value, could be anything as long as they are higher + // than max possible value (which would be A.length-1, since max + // swaps you can do is A.length-1)*/ + noSwap[i] = A.length; + swap[i] = A.length; + + if (A[i] > A[i - 1] && B[i] > B[i - 1]) { + /** + * We are here because this index does not need to swap. + // Great, what would be the cost to reach here and not swap? + // It'll be same as cost of not swapping for prev. index. + // Why dont we rather take min(noSwap[i-1], swap[i-1]), because + // in that case noSwap[i-1] will be min anyway. + */ + noSwap[i] = noSwap[i - 1]; + + + /** // what would be the cost to reach here and swap? + // It'll be cost of swapping for prev. index + 1. + // Why dont we rather take min(noSwap[i-1], swap[i-1]) + 1, because + // we are tracking optimistic swaps and if we do min, we will lose + // track of swaps that were needed and done in past. + */ + swap[i] = swap[i - 1] + 1; + + } + /** + * In this case, if we want to keep A and B increasing before the index i, can only have two choices. + * -> 2.1 swap at (i-1) and do not swap at i, we can get notswap[i] = Math.min(swap[i-1], notswap[i] ) + * -> 2.2 do not swap at (i-1) and swap at i, we can get swap[i]=Math.min(notswap[i-1]+1, swap[i]) + */ + if (A[i] > B[i - 1] && B[i] > A[i - 1]) { + noSwap[i] = Math.min(noSwap[i], swap[i - 1]); + swap[i] = Math.min(noSwap[i - 1] + 1, swap[i]); + } + } + + // Now that we cost of swapping and not swapping each index, + // answer is: + return Math.min(noSwap[A.length - 1], swap[A.length - 1]); + } + + public static void main(String[] args) { + + System.out.println(minSwap(new int[]{0, 4, 4, 5, 9}, new int[]{0, 1, 6, 8, 10})); + } +} diff --git a/src/main/java/dynamicProgramming/MonotoneIncreasingString.java b/src/main/java/dynamicProgramming/MonotoneIncreasingString.java new file mode 100644 index 0000000..45eaabb --- /dev/null +++ b/src/main/java/dynamicProgramming/MonotoneIncreasingString.java @@ -0,0 +1,78 @@ +package dynamicProgramming; + +/** + * https://leetcode.com/problems/flip-string-to-monotone-increasing + */ +public class MonotoneIncreasingString { + + /** + * * Algorithm: + * * + * * Skip 0's until we encounter the first 1. + * * Keep track of number of 1's in onesCount (Prefix). + * * Any 0 that comes after we encounter 1 can be a potential candidate for flip. Keep track of it in flipCount. + * * If flipCount exceeds oneCount - (Prefix 1's flipped to 0's) + * * a. Then we are trying to flip more 0's (suffix) than number of 1's (prefix) we have. + * * b. Its better to flip the 1's instead. + * @param s + * @return + */ + public int minFlipsMonoIncr(String s) { + char[] ch = s.toCharArray(); + int onescount = 0; + int flipcount = 0; + + for (int i = 0; i < s.length(); i++) { + if (ch[i] == '0') { + if (onescount == 0) continue; // if all beginning char is zero, no need to flip + flipcount++; // flip 0 -> 1 + } else { + onescount++; // flip 1 -> 0 + } + if (flipcount > onescount) { // if flip 0-> 1 greater than flip 1 -> 0 then + flipcount = onescount; // then we choose flip 1 -> 0 for the left part + } + } + + return flipcount; + } + + /** + * LIS variation + * Thought: + * Since s is a binary string, so an increasing subsequence will either: + * + * end at '0' + * end at '1' + * For number of changes(flips) to be minimum + * we need to find the longest part of the string which can be left untouched (i.e which is already increasing). + * + * Implementation Details: + * + * So if s[i]=='0': + * + * current element can extend an increasing subsequence ending at '0'. + * else if s[i]=='1' current element can extend: + * + * an increasing subsequence ending at a '0' + * an increasing subsequence ending at a '1' + * which one to choose ? the one which is the largest out of 1) and 2). + * answer=s.size() - max(lis ending at 0, lis ending at 1) + */ + public int MinFlipsMonoIncrLIS(String S) + { + int zeros = 0, increasingSeq = 0; + + for(int i = 0; i < S.length(); i++) + { + if((S.charAt(i) - '0') == 0) + zeros++; + else + increasingSeq++; + // it can extend both lis ending at 0 and ending at 1 so will choose the best of 2 + increasingSeq = Math.max(increasingSeq, zeros); + } + + return S.length() - increasingSeq; + } +} diff --git a/src/main/java/dynamicProgramming/NumberWithSameConsequtiveDifference.java b/src/main/java/dynamicProgramming/NumberWithSameConsequtiveDifference.java new file mode 100644 index 0000000..7ab5a1f --- /dev/null +++ b/src/main/java/dynamicProgramming/NumberWithSameConsequtiveDifference.java @@ -0,0 +1,57 @@ +package dynamicProgramming; + +import java.util.ArrayList; +import java.util.List; + +/** + * Return all non-negative integers of length N such that the absolute difference between every two consecutive digits is K. + * + * Note that every number in the answer must not have leading zeros except for the number 0 itself. + * For example, 01 has one leading zero and is invalid, but 0 is valid. + * + * You may return the answer in any order. + * + * Input: N = 3, K = 7 + * Output: [181,292,707,818,929] + * Explanation: Note that 070 is not a valid number, because it has leading zeroes. + * + * Input: N = 2, K = 1 + * Output: [10,12,21,23,32,34,43,45,54,56,65,67,76,78,87,89,98] + */ +public class NumberWithSameConsequtiveDifference { + + // the idea is to create a tree with dfs of depth N and in each step(level) we add and subract the K to + // the last element and check if it falls under the boundray (0> && <10) + // for N=3 and K=2 + // 1 + // -K / \ +K + // -2 3 + // / \ + // 1 5 --> end of tree because N=3 + + public int[] numsSameConsecDiff(int N, int K) { + List result= new ArrayList<>(); + if(N==1) result.add(0); + + for(int i=1;i<10;i++){ + dfs(N-1, K, result, i); + } + int[] ret = new int[result.size()]; + for(int i = 0;i < ret.length;i++) + ret[i] = result.get(i); + return ret; + } + + public void dfs(int N, int K, List result, int num){ + if(N==0) { + result.add(num); + return; + } + int lastDigit= num%10; + + if(lastDigit>=K) dfs(N-1,K,result,num*10+lastDigit-K); + if(K>0 && K+lastDigit<10) dfs(N-1,K,result,num*10+lastDigit+K); + } + + +} diff --git a/src/main/java/dynamicProgramming/OnesAndZeroes.java b/src/main/java/dynamicProgramming/OnesAndZeroes.java new file mode 100644 index 0000000..49e16a7 --- /dev/null +++ b/src/main/java/dynamicProgramming/OnesAndZeroes.java @@ -0,0 +1,45 @@ +package dynamicProgramming; + +/** + * https://leetcode.com/problems/ones-and-zeroes/ + */ +public class OnesAndZeroes { + private int[][][] memo; + + public int findMaxForm(String[] strs, int m, int n) { + memo = new int[m + 1][n + 1][strs.length]; + return findMaxFormFrom(strs, m, n, 0); + } + + private int findMaxFormFrom(String[] strs, int m, int n, int si) { + if (si == strs.length || (m == 0 && n == 0)) { + return 0; + } + if (memo[m][n][si] > 0) { + return memo[m][n][si]; + } + int cntIncludeStr = 0; + int zeros = countZeros(strs[si]); + int ones = strs[si].length() - zeros; + //for each string, we count the zeroes in it by countZeroesIn(String str) and see if there are enough 0s and 1s for it. + // If so, we accumulate that string and proceed with the remaining strings, 0s and 1s by means of the following code: + if (m >= zeros && n >= ones) { + cntIncludeStr = 1 + findMaxFormFrom(strs, m - zeros, n - ones, si + 1); + } + //We also take the other route, which simply skips the string and does not use any 0s and 1s. + int cntExcludeStr = findMaxFormFrom(strs, m, n, si + 1); + //Whichever is bigger, is that the result. + memo[m][n][si] = Math.max(cntIncludeStr, cntExcludeStr); + return memo[m][n][si]; + } + + private int countZeros(String s) { + int cntZero = 0; + for (char c : s.toCharArray()) { + if (c == '0') { + cntZero++; + } + } + return cntZero; + } +} diff --git a/src/main/java/dynamicProgramming/OptimalStratergy.java b/src/main/java/dynamicProgramming/OptimalStratergy.java new file mode 100644 index 0000000..5b47eb4 --- /dev/null +++ b/src/main/java/dynamicProgramming/OptimalStratergy.java @@ -0,0 +1,130 @@ +package dynamicProgramming; + +import java.util.Arrays; + +/** + * http://www.glassdoor.com/Interview/N-pots-each-with-some-number-of-gold-coins-are-arranged-in-a-line-You-are-playing-a-game-against-another-player-You-tak-QTN_350584.htm + *

+ * https://www.techiedelight.com/pots-gold-game-dynamic-programming/ + */ +public class OptimalStratergy { + // Function to maximize the number of coins collected by a player, + // assuming that opponent also plays optimally + public static int optimalStrategy(int[] coin, int i, int j, + int[][] lookup) { + // base case: one pot left, only one choice possible + if (i == j) { + return coin[i]; + } + + // if we're left with only two pots, choose one with maximum coins + if (i + 1 == j) { + return Integer.max(coin[i], coin[j]); + } + + // if sub-problem is seen for the first time, solve it and + // store its result in a lookup table + if (lookup[i][j] == 0) { + // if player chooses front coin i, opponent is left to choose + // from [i+1, j]. + // 1. if opponent chooses front coin i+1, recur for [i+2, j] + // 2. if opponent chooses rear coin j, recur for [i+1, j-1] + + int start = coin[i] + Integer.min(optimalStrategy(coin, i + 2, + j, lookup), + optimalStrategy(coin, i + 1, j - 1, lookup)); + + // if player chooses rear coin j, opponent is left to choose + // from [i, j-1]. + // 1. if opponent chooses front coin i, recur for [i+1, j-1] + // 2. if opponent chooses rear coin j-1, recur for [i, j-2] + + int end = coin[j] + Integer.min(optimalStrategy(coin, i + 1, + j - 1, lookup), + optimalStrategy(coin, i, j - 2, lookup)); + + // assign maximum of two choices + lookup[i][j] = Integer.max(start, end); + } + + // return the subproblem solution from the map + return lookup[i][j]; + } + + // main function + public static void main(String[] args) { + // pots of gold arranged in a line + int[] coin = {4, 6, 2, 3}; + + // Create a table to store solutions of subproblems + int[][] lookup = new int[coin.length][coin.length]; + + System.out.println("Maximum coins collected by player is " + + optimalStrategy(coin, 0, coin.length - 1, lookup)); + } + + public boolean PredictTheWinner(int[] nums) { + Integer[][] dp = new Integer[nums.length + 1][nums.length + 1]; + int playerA = recursionHelper(nums, 0, nums.length - 1, dp); + int playerB = Arrays.stream(nums).sum() - playerA; + + return playerA >= playerB; + } + + public int recursionHelper(int[] nums, int i, int j, Integer[][] dp) { + + if (i > j) return 0; + if (i == j) return nums[i]; + if (dp[i][j] != null) return dp[i][j]; + + int takeFront = nums[i] + Math.min(recursionHelper(nums, i + 2, j, dp), recursionHelper(nums, i + 1, j - 1, dp)); + int takeBack = nums[j] + Math.min(recursionHelper(nums, i + 1, j - 1, dp), recursionHelper(nums, i, j - 2, dp)); + return dp[i][j] = Math.max(takeFront, takeBack); + } + + public boolean PredictTheWinnerBottomUp(int[] nums) { + int n = nums.length; + + /* + dp[i][j] -> score of the first player for picks between nums[i..j] + */ + int[][] dp = new int[n][n]; + + // total of nums + int total = 0; + for (int num : nums) { + total += num; + } + + for (int len = 1; len <= n; len++) { + for (int i = 0; i + len <= n; i++) { + int j = i + len - 1; + + /* + First player has option to choose i or j + If he chooses i then 2nd player to choose in (i+1, j) + - if 2nd player chooses i+1, then player 1 will next choose from (i+2,j) + - if 2nd player chooses j, then player 1 will next choose from (i+1,j-1) + If he chooses j then 2nd player to choose in (i, j-1) + - if 2nd player chooses i, then player 1 will next choose from (i+1,j-1) + - if 2nd player chooses j-1, then player 1 will next choose from (i,j-2) + + We know that player 2 would have played wisely and player 1 will get the the minimum in the next move. + We choose the best(Max) of the above 2 scenarios + */ + int a = (i + 1 < n && j - 1 >= 0) ? dp[i + 1][j - 1] : 0; + int b = (i + 2 < n) ? dp[i + 2][j] : 0; + int c = (j - 2 >= 0) ? dp[i][j - 2] : 0; + + dp[i][j] = Math.max(nums[i] + Math.min(a, b), nums[j] + Math.min(a, c)); + } + } + + /* + dp[0][n-1] will have the score for the first player. + */ + int player1Score = dp[0][n - 1]; + int player2Score = total - player1Score; + return player1Score >= player2Score; + } +} \ No newline at end of file diff --git a/src/main/java/dynamicProgramming/OptimalTreeSearch.java b/src/main/java/dynamicProgramming/OptimalTreeSearch.java new file mode 100644 index 0000000..5f7412f --- /dev/null +++ b/src/main/java/dynamicProgramming/OptimalTreeSearch.java @@ -0,0 +1,47 @@ +package dynamicProgramming; +/** + * http://www.geeksforgeeks.org/dynamic-programming-set-24-optimal-binary-search-tree/ + */ +public class OptimalTreeSearch { + + public int minCost(int input[], int freq[]){ + int T[][] = new int[input.length][input.length]; + + for(int i=0; i < T.length; i++){ + T[i][i] = freq[i]; + } + + for(int l = 2; l <= input.length; l++){ + for(int i=0; i <= input.length-l; i++){ + int j = i + l -1; + T[i][j] = Integer.MAX_VALUE; + int sum = getSum(freq, i, j); + + for(int k=i; k <= j; k++){ + int val = sum + (k-1 < i ? 0 : T[i][k-1]) + + (k+1 > j ? 0 : T[k+1][j]) ; + if(val < T[i][j]){ + T[i][j] = val; + } + } + } + } + return T[0][input.length-1]; + } + + private int getSum(int freq[], int i, int j){ + int sum = 0; + for(int x = i; x <= j; x++){ + sum += freq[x]; + } + return sum; + } + + + public static void main(String args[]){ + int input[] = {10,12,20,35,46}; + int freq[] = {34,8,50,21,16}; + OptimalTreeSearch ots = new OptimalTreeSearch(); + System.out.println(ots.minCost(input, freq)); + } +} \ No newline at end of file diff --git a/src/main/java/dynamicProgramming/OutOfBounds.java b/src/main/java/dynamicProgramming/OutOfBounds.java new file mode 100644 index 0000000..50ddb73 --- /dev/null +++ b/src/main/java/dynamicProgramming/OutOfBounds.java @@ -0,0 +1,32 @@ +package dynamicProgramming; + +/** + * https://leetcode.com/problems/out-of-boundary-paths/ + */ +public class OutOfBounds { + int [][] dirs= new int[][]{{-1,0},{1,0},{0,-1},{0,1}}; + int mod = 1000000000 + 7; + Integer[][][] cache; + public int findPaths(int m, int n, int maxMove, int startRow, int startColumn) { + cache= new Integer[m+1][n+1][maxMove+1]; + return recursionHelper(m,n,maxMove,startRow,startColumn); + } + + public int recursionHelper(int m, int n, int maxMove, int startRow, int startColumn){ + + if(startRow<0 || startRow>=m || startColumn<0 || startColumn>=n) return 1; + if(maxMove==0) return 0; + + if(cache[startRow][startColumn][maxMove]!=null) return cache[startRow][startColumn][maxMove]; + + int result=0; + + for(int[] dir:dirs){ + result= result+recursionHelper(m,n,maxMove-1,startRow+dir[0],startColumn+dir[1]) %mod; + result%=mod; + } + + return cache[startRow][startColumn][maxMove]= result; + + } +} diff --git a/src/main/java/dynamicProgramming/PaintHouses.java b/src/main/java/dynamicProgramming/PaintHouses.java new file mode 100644 index 0000000..1ea1bc3 --- /dev/null +++ b/src/main/java/dynamicProgramming/PaintHouses.java @@ -0,0 +1,27 @@ +package dynamicProgramming; + +/** + * TODO + * https://leetcode.com/problems/paint-house/ + */ +public class PaintHouses { + + public int minCost(int[][] costs) { + if (costs == null || costs.length == 0) return 0; + // Assume all costs are positive + int n = costs.length; // number of houses + int[][] dp = new int[n][3]; + // Init + dp[0][0] = costs[0][0]; + dp[0][1] = costs[0][1]; + dp[0][2] = costs[0][2]; + // DP + for (int i = 1; i < n; ++i) { + dp[i][0] = Math.min(dp[i - 1][1], dp[i - 1][2]) + costs[i][0]; + dp[i][1] = Math.min(dp[i - 1][0], dp[i - 1][2]) + costs[i][1]; + dp[i][2] = Math.min(dp[i - 1][0], dp[i - 1][1]) + costs[i][2]; + } + return Math.min(dp[n - 1][0], Math.min(dp[n - 1][1], dp[n - 1][2])); + } + +} diff --git a/src/main/java/dynamicProgramming/PerfectSquare.java b/src/main/java/dynamicProgramming/PerfectSquare.java new file mode 100644 index 0000000..4c5e01d --- /dev/null +++ b/src/main/java/dynamicProgramming/PerfectSquare.java @@ -0,0 +1,86 @@ +package dynamicProgramming; + +import java.util.*; + +class PerfectSquare { + // let's solve by dynamic programming approach + // if we take value 13, the number of perfect squares less than + // the input at any point is Sqrt(input)=>(sqrt(13)=3, so max it can have answer below) + // 3,because 4*4 is 16, so perfect squares below 13 are (1*1, 2*2, 3*3) + // 13 can be broken down into + // 1/ 4| \ 9 deducting 1^2 leaves with val 12 + // / | \ deducting 2^2 leaves with val 9 + // 12 9 4 deducting 3^2 leaves with val 4 + // /|\ /|\ /| + // / | \ 8 5 0 3 0 + // 11 7 3 + + public int numSquaresDp(int n) { + int[] ns = new int[n+1]; + + for(int i=1;i<=n;i++){ + int min = i; // initial value is i because it can have i perfect squares(1^2 +1^2+...i) + int sqrt = (int)Math.sqrt(i); // because i can have max of sqrt(i) perfect squares + for(int j=sqrt;j>0;j--){ + int result = i - j*j; + // the reason to add 1 to the ns[result] is, we take away + // a square from 'i' initially (j*j) and check for best answer in the 1-D arr + // we add back the 'taken out' square to min value + // for e.x if i=13, sqrt is 3, while iterating + // we subtract 1*1 from 13 and check for best possible answer for 12 ns[result] + // finally we add back the 1*1 as +1 (ns[result]+1) + min = Math.min(min, ns[result]+1); + } + ns[i] = min; + System.out.println(Arrays.toString(ns)); + } + return ns[n]; + } + + public int numSquares(int n){ + Queue q = new LinkedList<>(); + Set visited = new HashSet<>(); + q.offer(0); + visited.add(0); + int depth = 0; + while (!q.isEmpty()) { + int size = q.size(); + depth++; + while (size > 0) { + int removed = q.poll(); + for (int i = 1; i * i <= n; i++) { + int v = removed + i * i; + if (v == n) { + return depth; + } + if (v > n) { + break; + } + if (!visited.contains(v)) { + q.offer(v); + visited.add(v); + } + } + size--; + } + } + return depth; + } + + Integer[]dp= new Integer[10000]; + public int numSquaresRecur(int n) { + if(n<=0) return 0; + if(n==1) return 1; + if(dp[n]!=null) return dp[n]; + int count=100000; + for(int i=1;i<=Math.sqrt(n);i++){ + count= Math.min(count, 1+numSquaresRecur(n-i*i)); + } + dp[n]=count; + return dp[n]; + } + + public static void main(String[] args) { + new PerfectSquare().numSquaresDp(13); + } +} \ No newline at end of file diff --git a/src/main/java/dynamicProgramming/StoneGame.java b/src/main/java/dynamicProgramming/StoneGame.java new file mode 100644 index 0000000..d65c68f --- /dev/null +++ b/src/main/java/dynamicProgramming/StoneGame.java @@ -0,0 +1,78 @@ +package dynamicProgramming; + +/** + * Pots of Gold + * https://leetcode.com/problems/stone-game + * + * Player A B A + * + * - pick index i+2 + * - pick index i+1 + * - pick index j + * - pick index i + * - pick index i + * - pick index j + * - pick index j-1 + * + * - pick index i+2 + * - pick index i + * - pick index j-1 + * - pick index j + * - pick index i + * - pick index j-1 + * - pick index j-2 + * + * For example, stones = [5, 3, 4, 5] + * + * If A picks left most stone 5, remain stones are [3, 4, 5]. + * + * B with choices to take left most stone 3 or right most stone 5, + * and B's best choice is to take right most stone 5 because 5 > 3 & 5 > 4. + * Then A has choice to make among remaining stones [3, 4]. + * + * From above decisions, what A can get for next to next round? + * If B takes left most stone 3, A's selectable range are [4, 5] + * If B takes right most stone 5, A's selectable range are [3, 4] + * + * And since B make choice to maximum B's score, + * choices A can have is not max of([4, 5], [3, 4]) which is [4, 5], but min([4, 5], [3, 4]), which is [3, 4]. + * + * This is a little complicated, + * but since B also wants to get maximum score, remaining choices for A is not max but min. + * + * Use memo for recursion, memo[i][j] means maximum score can get form index i ~j. + * The condition for A to win is to make sure either of one condition follows: + * memo[0][size-1] > max(memo[1][size-1], memo[0][size-2]). + */ +public class StoneGame { + + Integer[][] cache; + + public boolean stoneGame(int[] piles) { + cache = new Integer[piles.length + 2][piles.length + 2]; + int totalSum = 0; + for (int pile : piles) { + totalSum += pile; + } + + return dfsHelper(piles, 0, piles.length - 1) > totalSum / 2; + } + + public int dfsHelper(int[] piles, int startIndex, int endIndex) { + if (startIndex >= piles.length || endIndex < 0) return 0; + + if (cache[startIndex][endIndex] != null) return cache[startIndex][endIndex]; + + int maxStonesPickedFromStart= piles[startIndex] + Math.min( + dfsHelper(piles,startIndex+2,endIndex), + dfsHelper(piles,startIndex+1,endIndex-1) + ); + + int maxStonesPickedFromEnd = piles[endIndex] + Math.min( + dfsHelper(piles,startIndex,endIndex-2), + dfsHelper(piles,startIndex+1,endIndex-1) + ); + + return Math.max(maxStonesPickedFromEnd,maxStonesPickedFromStart); + } +} diff --git a/src/main/java/dynamicProgramming/TargetSum.java b/src/main/java/dynamicProgramming/TargetSum.java new file mode 100644 index 0000000..2a24d86 --- /dev/null +++ b/src/main/java/dynamicProgramming/TargetSum.java @@ -0,0 +1,111 @@ +package dynamicProgramming; + +/** + * https://leetcode.com/problems/target-sum/ + *

+ * Why 0/1 Knapsack? Our 'Capacity' is the target we want to reach 'S'. + * Our 'Items' are the numbers in the input subset and the 'Weights' of the items are the values of the numbers itself. + * This question follows 0/1 and not unbounded knapsack because we can use each number ONCE. + *

+ * What is the variation? The twist on this problem from standard knapsack is that + * we must add ALL items in the subset to our knapsack. + * We can reframe the question into adding the positive or negative + * value of the current number to our knapsack in order to reach the target capacity 'S'. + *

+ * What is the variation? The twist on this problem from standard knapsack is that + * we must add ALL items in the subset to our knapsack. + * We can reframe the question into adding the positive or negative value of the current number to our knapsack + * in order to reach the target capacity 'S'. + *

+ * We need 2 base cases. One for when the current state is valid and one for when the current state is invalid. + *

+ * Valid: Index is out of bounds AND current sum is equal to target 'S' + * Invalid: Index is out of bounds + *

+ *

+ * Given nums = [1, 2, 3, 4, 5] and target = 3 then one possible solution is +1-2+3-4+5 = 3 + * Here positive subset is P = [1, 3, 5] and negative subset is N = [2, 4] + *

+ * Then let's see how this can be converted to a subset sum problem: + *

+ * sum(P) - sum(N) = target + * sum(P) + sum(N) + sum(P) - sum(N) = target + sum(P) + sum(N) + * 2 * sum(P) = target + sum(nums) + */ +public class TargetSum { + Integer[][] cache; + + public int findTargetSumWays(int[] nums, int target) { + int sum = 0; + for (int i : nums) sum += i; + cache = new Integer[1001][2 * sum + 1]; + + // the reason we send target+sum and sum instead of target and 0 is + // sum-nums[index] will produce negative values which will throw index out of bound exception at cache[index][sum] + return recursionHelper(nums, target + sum, 0, sum); + } + + public int recursionHelper(int[] nums, int target, int index, int sum) { + + if (index >= nums.length) { + return target == sum ? 1 : 0; + } + + if (cache[index][sum] != null) return cache[index][sum]; + + int left = recursionHelper(nums, target, index + 1, sum + nums[index]); + + int right = recursionHelper(nums, target, index + 1, sum - nums[index]); + + return cache[index][sum] = left + right; + } + + /** solution 2: DP (0/1 knapsack) - Time: O(n^2), Space: O(n^2) Thanks @jerry */ + /** + * sub-problem: dp[i][j] represents number of possible ways to reach sum j by using first ith items + * base case: dp[0][sum], position sum represents sum 0 + * recurrence relation: + * dp[i][j] += dp[i - 1][j + nums[i - 1]] if j + nums[i - 1] <= sum * 2 + * dp[i][j] += dp[i - 1][j - nums[i - 1]] if j - nums[i - 1] >= 0 + *

+ * explanation: if j + nums[i - 1] or j - nums[i - 1] is in correct range, we can use the number nums[i - 1] + * to generate next state of dp array + */ + public static int findTargetSumWaysBottomUp(int[] nums, int S) { + if (nums.length == 0) { + return 0; + } + + int sum = 0; + for (int num : nums) { + sum += num; + } + + // corner case: when S is out of range [-sum, sum] + if (S < -sum || S > sum) { + return 0; + } + + int[][] dp = new int[nums.length + 1][sum * 2 + 1]; + dp[0][sum] = 1; + int leftBound = 0; + int rightBound = sum * 2; + for (int i = 1; i <= nums.length; i++) { + for (int j = leftBound; j < rightBound + 1; j++) { + // try all possible sum of (previous sum j + current number nums[i - 1]) and all possible difference of + // (previous sum j - current number nums[i - 1]) + if (j + nums[i - 1] <= rightBound) { + dp[i][j] += dp[i - 1][j + nums[i - 1]]; + } + if (j - nums[i - 1] >= leftBound) { + dp[i][j] += dp[i - 1][j - nums[i - 1]]; + } + } + } + return dp[nums.length][sum + S]; + } + + public static void main(String[] args) { + System.out.println(findTargetSumWaysBottomUp(new int[]{1, 1, 1, 1, 1}, 3)); + } +} diff --git a/src/main/java/dynamicProgramming/TriangleSum.java b/src/main/java/dynamicProgramming/TriangleSum.java new file mode 100644 index 0000000..65dd427 --- /dev/null +++ b/src/main/java/dynamicProgramming/TriangleSum.java @@ -0,0 +1,64 @@ +package dynamicProgramming; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * https://leetcode.com/problems/triangle/ + */ +public class TriangleSum { + static Integer[][] cache = null; + + public static int minimumTotal(List> triangle) { + cache = new Integer[triangle.size()][triangle.size()]; + + return recursionUtil(triangle, 0, 0); + } + + public static int recursionUtil(List> triangle, int triangleIndex, int subIndex) { + if (triangleIndex >= triangle.size()) return 0; + + if (cache[triangleIndex][subIndex] != null) return cache[triangleIndex][subIndex]; + + // recursively take from next rom same column + int left = recursionUtil(triangle, triangleIndex + 1, subIndex); + + // recursively take for next row, next column + int right = recursionUtil(triangle, triangleIndex + 1, subIndex + 1); + + cache[triangleIndex][subIndex] = Math.min(left, right) + triangle.get(triangleIndex).get(subIndex); + + return cache[triangleIndex][subIndex]; + } + + public static int minimumTotalBottomUp(List> triangle) { + + int[][] dp = new int[triangle.size()][triangle.size()]; + // the commented code is to optimise the O(N^2) space + // int[]dp = new int[triangle.size()]; + // int[]dp1 = new int[triangle.size()]; + + //Remember base case is just returning leaf nodes + for (int i = 0; i < triangle.size(); i++) { + dp[triangle.size() - 1][i] = triangle.get(triangle.size() - 1).get(i); + } + + for (int row = triangle.size() - 2; row >= 0; row--) { + for (int pos = 0; pos < triangle.get(row).size(); pos++) { + //dp1[pos] = triangle.get(row).get(pos) + Math.min(dp[pos+1], dp[pos]); + dp[row][pos] = triangle.get(row).get(pos) + Math.min(dp[row + 1][pos], dp[row + 1][pos + 1]); + } + //dp = dp1; + } + + return dp[0][0]; + } + + public static void main(String[] args) { + List> triangle = Arrays.stream(new Integer[][]{{2}, {3, 4}, {6, 5, 7}, {4, 1, 8, 3}}) + .map(Arrays::asList) + .collect(Collectors.toList()); + minimumTotalBottomUp(triangle); + } +} diff --git a/src/main/java/dynamicProgramming/TwoKeysKeyBoard.java b/src/main/java/dynamicProgramming/TwoKeysKeyBoard.java new file mode 100644 index 0000000..7a0b434 --- /dev/null +++ b/src/main/java/dynamicProgramming/TwoKeysKeyBoard.java @@ -0,0 +1,49 @@ +package dynamicProgramming; + +public class TwoKeysKeyBoard { + + /** + * the idea is if i is even(i%2), go to the i/2 position and add 2 (1- for copy and 1- for paste) + * if i is odd, we need to check what other odd is factor of i, initialise j and iterate till sqrt(i) + * if we find i%j==0 then we go to i%j position and add 1-for copy and j-1 for paste + * for e.x if i=9 we find j=3 (i%3==0), we go to i/j (3rd position which has 'AAA') and copy and paste it 2 times + * i=3(AAA), copy and paste 2 times (AAA-AAA-AAA) we get 9 items + * + */ + public static int minSteps(int n) { + int[] dp = new int[n+1]; + dp[1]=0; + for(int i=2;i<=n;i++){ + dp[i] = i; // this is for prime numbers + if(i%2==0){ + dp[i] = dp[i/2]+1+1; // 1 for copy + 1 for paste + }else{ + for(int j=2;j<=Math.sqrt(i);j++){ + if(i%j==0){ + dp[i] = Math.min(dp[i], dp[i/j] + 1 + j-1); //1 for copy and (j-1) for paste + } + } + } + + } + return dp[n]; + } + + int[] dp = new int[1001]; + public int minStepRecurs(int n) { + if(n==1) return 0; + if(dp[n]!=0) return dp[n]; + int min=n; + for(int i=2;i<=Math.sqrt(n);i++){ + if(n%i==0){ + min = Math.min(min, minStepRecurs(n/i) + 1 + i-1); //1 for copy and (i-1) for paste + } + } + dp[n] = min; + return min; + } + + public static void main(String[] args) { + System.out.println(minSteps(9)); + } +} diff --git a/src/main/java/dynamicProgramming/UniqueCoinChange.java b/src/main/java/dynamicProgramming/UniqueCoinChange.java new file mode 100644 index 0000000..aefecff --- /dev/null +++ b/src/main/java/dynamicProgramming/UniqueCoinChange.java @@ -0,0 +1,36 @@ +package dynamicProgramming; + + +/** + * https://leetcode.com/problems/coin-change + */ +public class UniqueCoinChange { + + public int changeSpaceOptimised(int amount, int[] coins) { + int[] combi = new int[amount + 1]; + combi[0] = 1; + for (int coin : coins) { + for (int j = 1; j <= amount; j++) { + if (j - coin >= 0) + combi[j]+= combi[j - coin]; + } + } + for (int a : combi) + System.out.print(a + " "); + return combi[amount]; + } + + public int changeRecursion(int amount, int[] coins) { + Integer[][] dp = new Integer[amount+1][coins.length+1]; + return recursionHelper(amount,0,coins,dp); + } + + public int recursionHelper(int amount, int idx, int[] coins,Integer[][] dp){ + if(amount==0) return 1; + if(amount<=0 || idx>=coins.length) return 0; + if(dp[amount][idx]!=null) return dp[amount][idx]; + + return dp[amount][idx]=recursionHelper(amount-coins[idx],idx,coins,dp)+recursionHelper(amount,idx+1,coins,dp); + } + +} diff --git a/src/main/java/dynamicProgramming/fibonacci/DecodeWays.java b/src/main/java/dynamicProgramming/fibonacci/DecodeWays.java new file mode 100644 index 0000000..7aa87b1 --- /dev/null +++ b/src/main/java/dynamicProgramming/fibonacci/DecodeWays.java @@ -0,0 +1,83 @@ +package dynamicProgramming.fibonacci; + + +/** + Case 1 : Pick single element, so in this we pick current and call for index + 1. + note : In case of single pick, element should not be '0' as it is invalid + + -> ways = decode(s, idx+1, n) + : elements in range [1,9] is covered here in this case + + Case 2 : Pick couple, so that we can get elements in range [10, 26] . + Catch here is that we need to check and validate values so that we do not exceed the range. + + */ +public class DecodeWays { + + public int numDecodings(String s) { + if (s == null || s.isEmpty()) { + return 0; + } + int n = s.length(); + int[] dp = new int[n + 1]; + dp[0] = 1; // an empty string can only be decoded as an empty string + dp[1] = s.charAt(0) != '0' ? 1 : 0; // If current element is 0, we simply return 0 as it is not possible to get a character using 0. + for (int i = 2; i <= n; i++) { + int first = Integer.parseInt(s.substring(i - 1, i)); + int second = Integer.parseInt(s.substring(i - 2, i)); + if (first >= 1 && first <= 9) { + dp[i] += dp[i - 1]; + } + if (second >= 10 && second <= 26) { + dp[i] += dp[i - 2]; + } + } + return dp[n]; + } + + public int numDecodings1(String s) { + if (s == null || s.isEmpty()) return 0; + Integer[] cache = new Integer[s.length() + 1]; + return helperFn(s, 0, cache); + } + + public int helperFn(String s, int index, Integer[] cache) { + //When we reach the end of the string this means that we have found a possible way to decode. + //Thus, this will contribute to answer and return 1. + if (index >= s.length()) return 1; + + if (cache[index] != null) return cache[index]; + int total = 0; + if (index + 1 <= s.length()) { + String temp1 = s.substring(index, index + 1); + if (valid(temp1)) { + total += helperFn(s, index + 1, cache); + } + } + + if (index + 2 <= s.length()) { + String temp2 = s.substring(index, index + 2); + if (valid(temp2)) { + total += helperFn(s, index + 2, cache); + } + } + + cache[index] = total; + return cache[index]; + + } + + public boolean valid(String s1) { + if (s1.isEmpty()) return false; + if (s1.charAt(0) == '0') return false; //If current element is 0, we simply return 0 as it is not possible to get a character using 0. + + int val = Integer.parseInt(s1); + + return val >= 1 && val <= 26; + } + + public static void main(String[] args) { + DecodeWays decode = new DecodeWays(); + System.out.println(decode.numDecodings("1210")); + } +} diff --git a/src/main/java/dynamicProgramming/fibonacci/DiceThrow.java b/src/main/java/dynamicProgramming/fibonacci/DiceThrow.java new file mode 100644 index 0000000..b176233 --- /dev/null +++ b/src/main/java/dynamicProgramming/fibonacci/DiceThrow.java @@ -0,0 +1,38 @@ +package dynamicProgramming.fibonacci; + +/** + * Time Complexity: O(m * n * x) + * https://leetcode.com/problems/number-of-dice-rolls-with-target-sum + */ +class DiceThrow { + // f-> faces + // d->dices + // target->sum + static Integer[][] cache = new Integer[31][1001]; + + public static int numRollsToTarget(int d, int f, int target) { + if (d <= 0 || target < 0) return target == 0 ? 1 : 0; + if (cache[d][target] != null) return cache[d][target]; + int ways = 0; + for (int i = 1; i <= f; i++) { + ways += numRollsToTarget(d - 1, f, target - i); + ways %= 1000000007; + } + + return cache[d][target] = ways; + } + + public int numRollsToTargetBottomUp(int d, int f, int target) { + int[][] dp = new int[d + 1][target + 1]; + dp[0][0] = 1; + for (int i = 1; i <= d; i++) + for (int j = 1; j <= target; j++) + for (int k = 1; k <= f; k++) + dp[i][j] = (dp[i][j] + (k <= j ? dp[i - 1][j - k] : 0)) % 1000000007; + return dp[d][target]; + } + + public static void main(String[] args) { + System.out.println(numRollsToTarget(6, 3, 6)); + } +} \ No newline at end of file diff --git a/src/main/java/dynamicProgramming/fibonacci/FibonacciStaircaseWaysToCoverDist.java b/src/main/java/dynamicProgramming/fibonacci/FibonacciStaircaseWaysToCoverDist.java new file mode 100644 index 0000000..09a0502 --- /dev/null +++ b/src/main/java/dynamicProgramming/fibonacci/FibonacciStaircaseWaysToCoverDist.java @@ -0,0 +1,110 @@ +package dynamicProgramming.fibonacci; + +import java.util.Arrays; +import java.util.HashMap; + +public class FibonacciStaircaseWaysToCoverDist { + + public int fibonacciSeriesRecursive(int n) { + if (n == 1) + return 2; + if (n == 2) + return 3; + return fibonacciSeriesRecursive(n - 1) + fibonacciSeriesRecursive(n - 2); + } + + public static void main(String args[]) { + FibonacciStaircaseWaysToCoverDist fs = new FibonacciStaircaseWaysToCoverDist(); + System.out.println(fs.fibonacciSeries(4)); + System.out.println(fs.fibonacciSeriesRecursive(3)); + } + + public int fibonacciSeries(int n) { + int n1 = 0; + int n2 = 1; + int sum; + + if (n == n1 || n == n2) { + return n; + } + + for (int i = 1; i <= n; i++) { + sum = n1 + n2; + n1 = n2; + n2 = sum; + } + return n2; + } + + + public int climbStairsBottomUp(int n) { + int[] dp = new int[n + 1]; + dp[0] = 0; + dp[1] = 1; + dp[2] = 2; + for (int stair = 3; stair <= n; ++stair) { + dp[stair] += dp[stair - 1] + dp[stair - 2]; + } + + return dp[n]; + } + + public int climbStairs(int N) { + int[] cache = new int[N + 1]; + Arrays.fill(cache, -1); + return fibUtil(N, 0, cache); + } + + public int fibUtil(int N, int start, int[] cache) { + if (start > N) return 0; + + if (N == start) return 1; + + if (cache[start] != -1) return cache[start]; + + cache[start] = fibUtil(N, start + 1, cache) + fibUtil(N, start + 2, cache); + + return cache[start]; + } + + + public int minCostClimbingStairs(int[] cost) { + if (cost.length == 2) return Math.min(cost[0], cost[1]); + int[] dp = new int[cost.length + 1]; + dp[0] = cost[0]; + dp[1] = cost[1]; + + for (int i = 2; i < cost.length; i++) { + dp[i] = Math.min(dp[i - 1], dp[i - 2]) + cost[i]; + } + + return Math.min(dp[cost.length - 1], dp[cost.length - 2]); + } + + private HashMap memo = new HashMap<>(); + + public int minCostClimbingStairsRecursion(int[] cost) { + return minimumCost(cost.length, cost); + } + + private int minimumCost(int i, int[] cost) { + // Base case, we are allowed to start at either step 0 or step 1 + if (i <= 1) { + return 0; + } + + // Check if we have already calculated minimumCost(i) + if (memo.containsKey(i)) { + return memo.get(i); + } + + // If not, cache the result in our hash map and return it + int downOne = cost[i - 1] + minimumCost(i - 1, cost); + int downTwo = cost[i - 2] + minimumCost(i - 2, cost); + memo.put(i, Math.min(downOne, downTwo)); + return memo.get(i); + } + +} + + diff --git a/src/main/java/dynamicProgramming/lcs/BitonicSequence.java b/src/main/java/dynamicProgramming/lcs/BitonicSequence.java new file mode 100644 index 0000000..b0a0959 --- /dev/null +++ b/src/main/java/dynamicProgramming/lcs/BitonicSequence.java @@ -0,0 +1,70 @@ +package dynamicProgramming.lcs; + +/** + * https://leetcode.com/problems/minimum-number-of-removals-to-make-mountain-array/ + * + * tricky LIS + */ +public class BitonicSequence { + /** + * Concept: We need to find the maximum number of elements of the array that can be + * involved in a mountain array. We know, that a mountain array contains a peak element + * and there is an increasing subsequence in the left of the peak and a decreasing subsequence in the right. + * So, we need to find out the element(peak), for which the total number of elements from the + * original array involved in the left increasing subsequence and the right decreasing + * subsequence, in maximum. This will create a mountain array with the peak element. + * Then, we can delete the rest of the elements of the array not involved in this mountain array. + */ + public int longestSequence(int arr[]) { + int[] lis = new int[arr.length]; + int[] lds = new int[arr.length]; + for (int i = 0; i < arr.length; i++) { + lis[i] = 1; + lds[i] = 1; + } + for (int i = 1; i < arr.length; i++) { + for (int j = 0; j < i; j++) { + if (arr[i] > arr[j]) { + lis[i] = Math.max(lis[i], lis[j] + 1); + } + } + } + + for (int i = arr.length - 2; i >= 0; i--) { + for (int j = arr.length - 1; j > i; j--) { + if (arr[i] > arr[j]) { + lds[i] = Math.max(lds[i], lds[j] + 1); + } + } + } + // because that middle element is common in both sequence .. for example, + // increasing subsequence 2,8,20 .. decreasing one 20,13,14 .. each of them has + // length 3 .. but bitonic subsequence 2,8,20,13,14 .. length= 3+3-1=5 + int max = 0; + for (int i = 0; i < arr.length; i++) { + /* + If the below conditional statement is not given, then strictly increasing or strictly + decreasing sequences will also be considered. It will hence fail in, + Test case: [10, 9, 8, 7, 6, 5, 4, 5, 4]. + ---Thanks to @chejianchao for suggesting the test case. + We need to make sure both the LIS on the left and right, ending at index i, + has length > 1. + */ + int max1 = lis[i] + lds[i] - 1; //Peak is counted twice in lis[] and lsd[] so -1 + System.out.print(max1 + " "); + if (max < max1) { + max = max1; + } + } + + return max; + } + + public static void main(String args[]) { + BitonicSequence bs = new BitonicSequence(); + int[] arr = {1, 4, 3, 7, 2, 1, 8, 11, 13, 0}; + int r = bs.longestSequence(arr); + System.out.println(r); + + } +} \ No newline at end of file diff --git a/src/main/java/dynamicProgramming/lcs/DeleteAndEarn.java b/src/main/java/dynamicProgramming/lcs/DeleteAndEarn.java new file mode 100644 index 0000000..a0f4123 --- /dev/null +++ b/src/main/java/dynamicProgramming/lcs/DeleteAndEarn.java @@ -0,0 +1,70 @@ +package dynamicProgramming.lcs; + +import java.util.Arrays; + +public class DeleteAndEarn { + + public int deleteAndEarn(int[] nums) { + // if we sort the array, we do not need to delete elements smaller than nums[idx] (ie nums[idx] - 1) because they are already computed + // and saved in memo, we only need to delete nums[idx] + 1 and we can do this simply by skipping them since the array is sorted + Arrays.sort(nums); + Integer[] dp = new Integer[nums.length]; + return recursionUtil(nums, 0, dp); + } + + private int recursionUtil(int[] nums, int idx, Integer[] dp) { + // if we reached the end of the array, we can not earn anymore, return 0 + if (idx == nums.length) + return 0; + + if (dp[idx] != null) return dp[idx]; + // delete and earn this element + int earned = nums[idx]; + int skip = idx + 1; + + // simply add all similar values of nums[idx] to earned at once + while (skip < nums.length && nums[skip] == nums[idx]) { + earned += nums[idx]; + skip++; + } + + // skip all elements = nums[idx] + 1 + // this is instead of deleting the elements = nums[idx] + 1 + // which does not alter the array and make the solution work + while (skip < nums.length && nums[skip] == nums[idx] + 1) + skip++; + + // recurse + earned += recursionUtil(nums, skip, dp); + + // skip deleting and earning this element + int skipped = recursionUtil(nums, idx + 1, dp); + + // return the max of the 2 values + dp[idx] = Math.max(earned, skipped); + + + return dp[idx]; + } + + /** + * for numbers from [1 - 10000], each has a total sum sums[i]; if you earn sums[i], you cannot earn sums[i-1] and sums[i+1] + * kind of like house robbing. you cannot rob 2 connected houses. + */ + + public int deleteAndEarnBottomUp(int[] nums) { + int n = 10001; + int[] values = new int[n]; + for (int num : nums) + values[num] += num; + + int take = 0, skip = 0; + for (final int value : values) { + + final int temp = Math.max(skip + value, take); + skip = take; + take = temp; + } + return take; + } +} diff --git a/src/main/java/dynamicProgramming/lcs/EditDistance.java b/src/main/java/dynamicProgramming/lcs/EditDistance.java new file mode 100644 index 0000000..a84ee9b --- /dev/null +++ b/src/main/java/dynamicProgramming/lcs/EditDistance.java @@ -0,0 +1,135 @@ +package dynamicProgramming.lcs; + +/** + * + * @author Tushar Roy + * + * Given two strings how many minimum edits(update, delete or add) is + * needed to convert one string to another + * + * Time complexity is O(m*n) Space complexity is O(m*n) + * + * References: + * http://www.geeksforgeeks.org/dynamic-programming-set-5-edit-distance/ + * https://en.wikipedia.org/wiki/Edit_distance + */ +public class EditDistance { + + + /** + * Uses bottom up DP to find the edit distance + */ + public int dynamicEditDistance(char[] str1, char[] str2) { + int temp[][] = new int[str1.length + 1][str2.length + 1]; + + for (int i = 0; i < temp[0].length; i++) { + temp[0][i] = i; // it's number of edits to make top row empty, remember dp[0][0]="" empty char + } + + for (int i = 0; i < temp.length; i++) { + temp[i][0] = i; // it's number of edits to make empty char into first col values, remember dp[0][0]="" empty char + } + + for (int i = 1; i <= str1.length; i++) { + for (int j = 1; j <= str2.length; j++) { + if (str1[i - 1] == str2[j - 1]) { + temp[i][j] = temp[i - 1][j - 1]; + } else { + temp[i][j] = 1 + min(temp[i - 1][j - 1], temp[i - 1][j], temp[i][j - 1]); + } + } + } + printActualEdits(temp, str1, str2); + return temp[str1.length][str2.length]; + + } + + /** + * Prints the actual edits which needs to be done. + */ + public void printActualEdits(int T[][], char[] str1, char[] str2) { + int i = T.length - 1; + int j = T[0].length - 1; + while (true) { + if (i == 0 || j == 0) { + break; + } + if (str1[i - 1] == str2[j - 1]) { + i = i - 1; + j = j - 1; + } else if (T[i][j] == T[i - 1][j - 1] + 1) { + System.out.println("Edit " + str2[j - 1] + " in string2 to " + str1[i - 1] + " in string1"); + i = i - 1; + j = j - 1; + } else if (T[i][j] == T[i - 1][j] + 1) { + System.out.println("Delete in string1 " + str1[i - 1]); + i = i - 1; + } else if (T[i][j] == T[i][j - 1] + 1) { + System.out.println("Delete in string2 " + str2[j - 1]); + j = j - 1; + } else { + throw new IllegalArgumentException("Some wrong with given data"); + } + } + } + + public int minDistance(String word1, String word2) { + if(word1==null) return word2.length(); + if(word2==null) return word1.length(); + + int[][] dp= new int[word1.length()+1][word2.length()+1]; + + for(int i = 0;i<=word1.length();i++) dp[i][0] = i; + for(int j = 0;j<=word2.length();j++) dp[0][j] = j; + + for(int i=1;i<=word1.length();i++){ + for(int j=1; j<= word2.length(); j++){ + if(word1.charAt(i-1)==word2.charAt(j-1)){ + dp[i][j]=dp[i-1][j-1]; + }else{ + dp[i][j]= 1+Math.min(dp[i-1][j-1], Math.min(dp[i-1][j], dp[i][j-1])); + } + } + } + + return dp[word1.length()][word2.length()]; + } + + Integer[][] cache; + public int minDistanceTopDown(String word1, String word2) { + cache= new Integer[word1.length()][word2.length()]; + return recursionHelper(word1,word2,0,0); + } + + public int recursionHelper(String word1, String word2, int idx1, int idx2){ + if(idx1>=word1.length()) return word2.length()-idx2; + if(idx2>=word2.length()) return word1.length()-idx1; + + if(cache[idx1][idx2]!=null) return cache[idx1][idx2]; + + int result=0; + if(word1.charAt(idx1)==word2.charAt(idx2)){ + result+= recursionHelper(word1,word2,idx1+1,idx2+1); + }else{ + result+= 1+ Math.min(recursionHelper(word1,word2,idx1+1,idx2+1), + Math.min(recursionHelper(word1,word2,idx1+1,idx2),recursionHelper(word1,word2,idx1,idx2+1))); + } + + return cache[idx1][idx2]=result; + } + private int min(int a, int b, int c) { + int l = Math.min(a, b); + return Math.min(l, c); + } + + public static void main(String args[]) { + String str1 = "abcd"; + String str2 = "bcad"; + EditDistance editDistance = new EditDistance(); + int result = editDistance.dynamicEditDistance(str1.toCharArray(), str2.toCharArray()); + System.out.print(result); + } + +} + + diff --git a/src/main/java/dynamicProgramming/lcs/LongestCommonSubsequence.java b/src/main/java/dynamicProgramming/lcs/LongestCommonSubsequence.java new file mode 100644 index 0000000..302a9ab --- /dev/null +++ b/src/main/java/dynamicProgramming/lcs/LongestCommonSubsequence.java @@ -0,0 +1,72 @@ +package dynamicProgramming.lcs; + +import java.util.Arrays; + +public class LongestCommonSubsequence { + + // mistake i normally would assume is dp[0][0]=1, but actually it's not + + public static void main(String[] args) { + String str1 = "ABCD"; + String str2 = "AEDB"; + + System.out.println(longestCommonSubSeqPrint(str1, str2)); + + } + + public static int longestCommonSubsequence(String text1, String text2) { + if (text1 == null || text2 == null) return 0; + + int[][] dp = new int[text1.length() + 1][text2.length() + 1]; + + for (int i = 1; i <= text1.length(); i++) { + for (int j = 1; j <= text2.length(); j++) { + if (text1.charAt(i - 1) == text2.charAt(j - 1)) { + dp[i][j] = 1 + dp[i - 1][j - 1]; // previously matched characters + } else { + dp[i][j] = Math.max(dp[i][j - 1], dp[i - 1][j]); + } + } + } + + return dp[text1.length()][text2.length()]; + } + + Integer[][] cache; + + public int longestCommonSubsequenceTopDown(String text1, String text2) { + cache = new Integer[text1.length() + 1][text2.length() + 1]; + return recursionHelper(text1, text2, text1.length() - 1, text2.length() - 1); + } + + public int recursionHelper(String text1, String text2, int index1, int index2) { + if (index1 < 0 || index2 < 0) return 0; + if (cache[index1][index2] != null) return cache[index1][index2]; + + if (text1.charAt(index1) == text2.charAt(index2)) { + return cache[index1][index2] = 1 + recursionHelper(text1, text2, index1 - 1, index2 - 1); + } else { + return cache[index1][index2] = Math.max(recursionHelper(text1, text2, index1 - 1, index2),recursionHelper(text1, text2, index1, index2 - 1)); + } + } + + private static String longestCommonSubSeqPrint(String str1, String str2) { + String[][] dp = new String[str1.length() + 1][str2.length() + 1]; + for (String[] strings : dp) { + Arrays.fill(strings, ""); + } + for (int i = 1; i <= str1.length(); i++) { + for (int j = 1; j <= str2.length(); j++) { + if (str1.charAt(i - 1) == str2.charAt(j - 1)) { + dp[i][j] = dp[i - 1][j - 1] + str1.charAt(i - 1); + } else { + dp[i][j] = dp[i - 1][j].length() > dp[i][j - 1].length() ? dp[i - 1][j] : dp[i][j - 1]; + } + } + } + return dp[str1.length()][str2.length()]; + } + +} + + diff --git a/src/main/java/dynamicProgramming/lcs/LongestCommonSubstring.java b/src/main/java/dynamicProgramming/lcs/LongestCommonSubstring.java new file mode 100644 index 0000000..245c1bd --- /dev/null +++ b/src/main/java/dynamicProgramming/lcs/LongestCommonSubstring.java @@ -0,0 +1,37 @@ +package dynamicProgramming.lcs; + +import java.util.Deque; +import java.util.LinkedList; +import java.util.Queue; + +/** + * http://en.wikipedia.org/wiki/Longest_common_substring_problem + */ +public class LongestCommonSubstring { + + public int longestCommonSubstring(char str1[], char str2[]) { + int T[][] = new int[str1.length + 1][str2.length + 1]; + + int max = 0; + for (int i = 1; i <= str1.length; i++) { + for (int j = 1; j <= str2.length; j++) { + if (str1[i - 1] == str2[j - 1]) { + T[i][j] = T[i - 1][j - 1] + 1; + if (max < T[i][j]) { + max = T[i][j]; + } + } + } + } + + return max; + } + + public static void main(String args[]) { + LongestCommonSubstring lcs = new LongestCommonSubstring(); + char str1[] = "abcdef".toCharArray(); + char str2[] = "zcdemf".toCharArray(); + System.out.println(lcs.longestCommonSubstring(str1, str2)); + } + +} \ No newline at end of file diff --git a/src/main/java/dynamicProgramming/lcs/LongestIncreasingSubsequence.java b/src/main/java/dynamicProgramming/lcs/LongestIncreasingSubsequence.java new file mode 100644 index 0000000..d841989 --- /dev/null +++ b/src/main/java/dynamicProgramming/lcs/LongestIncreasingSubsequence.java @@ -0,0 +1,133 @@ +package dynamicProgramming.lcs; + +import java.util.Arrays; + +/** + * Solve the LIS subproblem for each snippet of the array ending between 1, 2, + * 3, ... and so on until nums.length - 1 (inclusive) + *

+ * Ex: + *

+ * [-2, 1, 2, 3] + *

+ * [-2] from index 0 to index 0 [-2, 1] from index 0 to index 1 [-2, 1, 2] from + * index 0 to index 2 [-2, 1, 2, 3] from index 0 to index 3 + *

+ * Our answer is the maximum LNDS found between all subproblems we solve along + * the way. + *

+ * Time complexity is O(n^2). + */ +public class LongestIncreasingSubsequence { + + public static void main(String[] args) { + int[] nums = {10, 22, 9, 33, 21, 50, 41, 60, 80}; + lisLength(nums); + } + + private static void lisLength(int[] nums) { + int[] result = new int[nums.length]; + Arrays.fill(result, 1); + int maximumSoFar = 1; + for (int i = 1; i < nums.length; i++) { + for (int j = 0; j < i; j++) { + if (nums[i] > nums[j]) { + result[i] = Math.max(result[i], result[j] + 1); + } + } + maximumSoFar = Math.max(maximumSoFar, result[i]); + } + System.out.println(Arrays.toString(result)); + System.out.println(maximumSoFar); + } + + public int lengthOfLISBottomUp(int[] nums) { + //there were two chaning variable in recursive solution curr and prev + //so we need a 2d matrix + //length decision:-prev will go max upto nums.length so will take nums.length+1 + // curr will go max upto nums.length-1 so will take nums.length + //for initialization of dp matrix initialize it with -1 + + int[][] dp = new int[nums.length + 1][nums.length]; + for (int[] x : dp) { + Arrays.fill(x, -1); + } + return solve(nums, -1, 0, dp); + } + + public int solve(int[] nums, int prevIndex, int curr, int[][] dp) { + if (curr == nums.length) { + return 0; + } + + if (dp[prevIndex + 1][curr] != -1) { + return dp[prevIndex + 1][curr]; + } + + if (prevIndex < 0 || nums[curr] > nums[prevIndex]) { + dp[prevIndex + 1][curr] = Math.max(1 + solve(nums, curr, curr + 1, dp), + solve(nums, prevIndex, curr + 1, dp)); + } else { + dp[prevIndex + 1][curr] = solve(nums, prevIndex, curr + 1, dp); + } + return dp[prevIndex + 1][curr]; + } + + /** + * https://www.youtube.com/watch?v=qW1O1a40-No&ab_channel=AryanMittal + *

+ * Idea is to prepare a result array, the result array will be sorted obviously because, we are looking for + * LIS. + * case i) if current element arr[i] is greater that result[result.length-1] append to result + * case ii) else we need to look for a suitable position in the result arr for that element arr[i] + * which has arr[i-1]>=arr[i]= + * arr[]= {2,5,3,7,11,8,10,13,6} + * result[]={} + * when i=2(arr[i]=3) + * result=[2,5] + * we need to see a position for 3 to accommodate, do a binary search in [2,5], we'll get index 1 + * so we modify the result = [2,3] *Note the length is not changed + *

+ * when i=6 (arr[i]=8) + * result=[2,3,7,11] we need to check for a position, do a binary search in [2,3,6,7], we'll get index 3 + * result=[2,3,7,8] + *

+ * when i=8(arr[i]=6) + * result=[2,3,7,8,10,13] or [2,5,7,8,10,13] do a binary search , we'll get index 2 + * result=[2,3,6,8,10,13] at this point we are not bothered about the correctness of result + * all we need is the length + */ + public static int lengthOfLIS(int[] nums) { + if (nums == null || nums.length == 0) + return 0; + + int n = nums.length, len = 0; + int[] increasingSequence = new int[n]; + increasingSequence[len++] = nums[0]; + for (int i = 1; i < n; i++) { + if (nums[i] > increasingSequence[len - 1]) + increasingSequence[len++] = nums[i]; + else { + int position = findPositionToReplace(increasingSequence, 0, len - 1, nums[i]); + increasingSequence[position] = nums[i]; + } + } + return len; + } + + public static int findPositionToReplace(int[] a, int low, int high, int x) { + int mid; + while (low < high) { + mid = low + (high - low) / 2; + if (a[mid] < x) { + low = mid + 1; + } else { + high = mid; + } + } + return low; + } + +} diff --git a/src/main/java/dynamicProgramming/lcs/LongestStringChain.java b/src/main/java/dynamicProgramming/lcs/LongestStringChain.java new file mode 100644 index 0000000..c46305a --- /dev/null +++ b/src/main/java/dynamicProgramming/lcs/LongestStringChain.java @@ -0,0 +1,89 @@ +package dynamicProgramming.lcs; + +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * https://leetcode.com/problems/longest-string-chain/ + */ +public class LongestStringChain { + + /** + * Algorithm + * Initialize a set (wordsPresent) and add all the words in the list to the set. This set will be used to check if a word is present in the list. + * Initialize a map (memo) having key type as String and value type as Integer. This map will store the length of the longest possible word sequence where the key is the last word in the sequence. + * Iterate over the list. For each word in the list perform a depth-first search. + * In the DFS, consider the current word (currentWord) as the last word in the word sequence. + * If currentWord was encountered previously we just return its corresponding value in the map memo. + * Initialize maxLength to 1. + * Iterate over the entire length of the currentWord. + * Create all possible words (newWord) by taking out one character at a time. + * If newWord is present in the set perform a DFS with this word and store the intermediate result in a variable currentLength. + * Update the maxLength so that it contains the length of the longest sequence possible where the currentWord is the end word. + * Set the maxLength as the value for currentWord (key) in the map. + * Return maxLength. + */ + private int dfs(Set words, Map memo, String currentWord) { + // If the word is encountered previously we just return its value present in the map (memoization). + if (memo.containsKey(currentWord)) { + return memo.get(currentWord); + } + // This stores the maximum length of word sequence possible with the 'currentWord' as the + int maxLength = 1; + StringBuilder sb = new StringBuilder(currentWord); + + // creating all possible strings taking out one character at a time from the `currentWord` + for (int i = 0; i < currentWord.length(); i++) { + sb.deleteCharAt(i); + String newWord = sb.toString(); + // If the new word formed is present in the list, we do a dfs search with this newWord. + if (words.contains(newWord)) { + int currentLength = 1 + dfs(words, memo, newWord); + maxLength = Math.max(maxLength, currentLength); + } + sb.insert(i, currentWord.charAt(i)); + } + memo.put(currentWord, maxLength); + + return maxLength; + } + + public int longestStrChain(String[] words) { + Map memo = new HashMap<>(); + Set wordsPresent = new HashSet<>(); + Collections.addAll(wordsPresent, words); + int ans = 0; + for (String word : words) { + ans = Math.max(ans, dfs(wordsPresent, memo, word)); + } + return ans; + } + + public int longestStrChainBottomUp(String[] words) { + Map cache = new HashMap<>(); + + + Arrays.sort(words, (a, b) -> Integer.compare(a.length(), b.length())); + + int result = 0; + + for (String word : words) { + int count = 0; + + for (int i = 0; i < word.length(); i++) { + String temp = new StringBuilder(word).deleteCharAt(i).toString(); + count = Math.max(count, cache.getOrDefault(temp, 0) + 1); + + } + + cache.put(word, count); + result = Math.max(result, count); + } + + return result; + } +} diff --git a/src/main/java/dynamicProgramming/lcs/MaximumContiguousSubarraySum.java b/src/main/java/dynamicProgramming/lcs/MaximumContiguousSubarraySum.java new file mode 100644 index 0000000..83170fe --- /dev/null +++ b/src/main/java/dynamicProgramming/lcs/MaximumContiguousSubarraySum.java @@ -0,0 +1,28 @@ +package dynamicProgramming.lcs; + +/** + * + * http://tinyurl.com/y4mffrn7 + */ +public class MaximumContiguousSubarraySum { + + public static void main(String[] args) { + int[] arr2 = { -6, 2, -4, 1, 3, -1, 5, -1 }; + MaximumContiguousSubarraySum mcs = new MaximumContiguousSubarraySum(); + System.out.println(mcs.maxSubArray(arr2)); + + } + + public int maxSubArray(int[] nums) { + + int maxSoFar = nums[0]; + int maxEndingHere = nums[0]; + + for (int i = 1; i < nums.length; i++) { + maxEndingHere = Math.max(maxEndingHere + nums[i], nums[i]); + maxSoFar = Math.max(maxSoFar, maxEndingHere); + } + return maxSoFar; + } + +} diff --git a/src/main/java/dynamicProgramming/lcs/MaximumLengthRepeatedSubarray.java b/src/main/java/dynamicProgramming/lcs/MaximumLengthRepeatedSubarray.java new file mode 100644 index 0000000..c961272 --- /dev/null +++ b/src/main/java/dynamicProgramming/lcs/MaximumLengthRepeatedSubarray.java @@ -0,0 +1,25 @@ +package dynamicProgramming.lcs; + +/** + * https://leetcode.com/problems/maximum-length-of-repeated-subarray/ + * + * Idea is same as Longest common substring, in case of subsequence only we'll do if(nums1[i-1]!=nums2[j-1]) + */ +public class MaximumLengthRepeatedSubarray { + + public int findLength(int[] nums1, int[] nums2) { + int[][] dp = new int[nums1.length + 1][nums2.length + 1]; + + int result = 0; + for (int i = 1; i <= nums1.length; i++) { + for (int j = 1; j <= nums2.length; j++) { + if (nums1[i - 1] == nums2[j - 1]) { + dp[i][j] = 1 + dp[i - 1][j - 1]; + result = Math.max(result, dp[i][j]); + } + } + } + + return result; + } +} diff --git a/src/main/java/dynamicProgramming/lcs/MaximumSumIncreasingSubsequence.java b/src/main/java/dynamicProgramming/lcs/MaximumSumIncreasingSubsequence.java new file mode 100644 index 0000000..7001da6 --- /dev/null +++ b/src/main/java/dynamicProgramming/lcs/MaximumSumIncreasingSubsequence.java @@ -0,0 +1,28 @@ +package dynamicProgramming.lcs; + +class MaximumSumIncreasingSubsequence { + + static int maxSumIS(int arr[], int n) { + int i, j, max = 0; + int msis[] = new int[n]; + + for (i = 0; i < n; i++) + msis[i] = arr[i]; + + for (i = 1; i < n; i++) { + for (j = 0; j < i; j++) { + System.out.println(arr[i] + ">" + arr[j] + "&&" + msis[i] + "<" + (msis[j] + arr[i])); + if (arr[i] > arr[j] && msis[i] < msis[j] + arr[i]) + msis[i] = msis[j] + arr[i]; + max = Math.max(max,msis[i]); + } + } + return max; + } + + public static void main(String args[]) { + int[] arr = new int[] { 1, 1001, 2, 3, 100, 4, 5 }; + int n = arr.length; + System.out.println("Sum of maximum sum " + "increasing subsequence is " + maxSumIS(arr, n)); + } +} \ No newline at end of file diff --git a/src/main/java/dynamicProgramming/lcs/MinimumAsciiDelete.java b/src/main/java/dynamicProgramming/lcs/MinimumAsciiDelete.java new file mode 100644 index 0000000..098262d --- /dev/null +++ b/src/main/java/dynamicProgramming/lcs/MinimumAsciiDelete.java @@ -0,0 +1,96 @@ +package dynamicProgramming.lcs; + +/** + * https://leetcode.com/problems/minimum-ascii-delete-sum-for-two-strings/ + *

+ * In this question we are given two strings , and asked to get the sum of ASCII values, to make string same, + * RE-WORDING question : get the sum of all characters which do not form longest Common Subsequence. + *

+ * +------+-----+-------+------+------+ + * | | "" | s (1) | e(2) | e(3) | + * +------+-----+-------+------+------+ + * | "" | 0 | 101 | 198 | 314 | + * +------+-----+-------+------+------+ + * | t(1) | 115 | 216 | 313 | 429 | + * +------+-----+-------+------+------+ + * | e(2) | 216 | 115 | 212 | 328 | + * +------+-----+-------+------+------+ + * | e(3) | 313 | | | | + * +------+-----+-------+------+------+ + */ +public class MinimumAsciiDelete { + + + public int minimumDeleteSumTopDown(String s1, String s2) { + int[][] dp = new int[s1.length() + 1][s2.length() + 1]; + + // see first row and col, in order to equate chars to "", we need to delete chars + // so we add all chars + //if string_A or string_B is empty : then our ans is sum of all ASCII of non empty string + for (int i = 1; i <= s2.length(); i++) { + dp[0][i] = dp[0][i - 1] + s2.charAt(i - 1); + } + // refer above comment + for (int i = 1; i <= s1.length(); i++) { + dp[i][0] = dp[i - 1][0] + s1.charAt(i - 1); + } + + for (int i = 1; i <= s1.length(); i++) { + for (int j = 1; j <= s2.length(); j++) { + //Of the two strings, if both of their last characters match + // then certainly the answer comes from skipping those characters. + //i.e. Answer("zca","bza") = Answer("zc","bz") + if (s1.charAt(i - 1) == s2.charAt(j - 1)) { + dp[i][j] = dp[i - 1][j - 1]; + } else { + //if the last characters are different then its one of the three situations: + //drop s1's last character (ASCII(s1's last) + dp[i-1][j]) + //drop s2's last character (ASCII(s2's last) + dp[i][j-1]) + // min of above 2 + //One is to delete s1.charAt(i-1) ,based on the condition that s1[0:i-1] and s2[0:j] is already the same. + //The other is to delete s2.charAt(j-1),based on the condition that s1[0:i] and s2[0:j-1] is already the same. + dp[i][j] = Math.min(dp[i - 1][j] + s1.charAt(i - 1), dp[i][j - 1] + s2.charAt(j - 1)); + } + } + } + + return dp[s1.length()][s2.length()]; + } + + + Integer[][] cache; + + public int minimumDeleteSum(String s1, String s2) { + cache = new Integer[s1.length() + 1][s2.length() + 1]; + + return recursionHelper(s1, s2, 0, 0); + + } + + public int recursionHelper(String s1, String s2, int i, int j) { + if (i >= s1.length() && j >= s2.length()) return 0; + if (cache[i][j] != null) return cache[i][j]; + if (i >= s1.length()) return getAscii(s2.substring(j)); + if (j >= s2.length()) return getAscii(s1.substring(i)); + + int result = 0; + + if (s1.charAt(i) == s2.charAt(j)) { + result += recursionHelper(s1, s2, i + 1, j + 1); + } else { + result += Math.min(recursionHelper(s1, s2, i + 1, j) + s1.charAt(i), recursionHelper(s1, s2, i, j + 1) + s2.charAt(j)); + } + + return cache[i][j] = result; + + } + + public int getAscii(String str) { + int res = 0; + for (char s : str.toCharArray()) { + res += s; + } + return res; + } + +} diff --git a/src/main/java/dynamicProgramming/lcs/NumberOfDistinctSubSequence.java b/src/main/java/dynamicProgramming/lcs/NumberOfDistinctSubSequence.java new file mode 100644 index 0000000..22f967c --- /dev/null +++ b/src/main/java/dynamicProgramming/lcs/NumberOfDistinctSubSequence.java @@ -0,0 +1,56 @@ +package dynamicProgramming.lcs; + +public class NumberOfDistinctSubSequence { + + Integer[][] cache; + + public int numDistinct(String s, String t) { + cache = new Integer[s.length() + 1][t.length() + 1]; + return recursionHelper(s, t, 0, 0); + } + + public int recursionHelper(String s, String t, int idx1, int idx2) { + + if (cache[idx1][idx2] != null) return cache[idx1][idx2]; + + if (idx1 == s.length() && idx2 < t.length()) return 0; + if (idx2 == t.length()) return 1; + + int result = 0; + // if both chars are same we skip both, and also we explore by skipping the source index to find the target char elsewhere + if (s.charAt(idx1) == t.charAt(idx2)) { + result = recursionHelper(s, t, idx1 + 1, idx2 + 1) + recursionHelper(s, t, idx1 + 1, idx2); + }else{ + // else we explore by skipping source index only because it's total ways + result += recursionHelper(s, t, idx1 + 1, idx2); + } + + + return cache[idx1][idx2] = result; + } + + /** + * Copy the recurrence relation into tabulation that's all is the trick + */ + public int numDistinctTopDown(String s, String t) { + + int[][] dp = new int[s.length()+1][t.length()+1]; + + for(int i=0;i<=s.length();i++){ + dp[i][0]=1; + } + + for(int i=1;i<=s.length();i++){ + for(int j=1;j<=t.length();j++){ + + if(s.charAt(i-1)==t.charAt(j-1)){ + dp[i][j] = dp[i-1][j-1]+dp[i-1][j]; + }else{ + dp[i][j] = dp[i-1][j]; + } + } + } + + return dp[s.length()][t.length()]; + } +} diff --git a/src/main/java/dynamicProgramming/lcs/NumberOfLIS.java b/src/main/java/dynamicProgramming/lcs/NumberOfLIS.java new file mode 100644 index 0000000..7d56c21 --- /dev/null +++ b/src/main/java/dynamicProgramming/lcs/NumberOfLIS.java @@ -0,0 +1,76 @@ +package dynamicProgramming.lcs; + +import java.util.Arrays; + +/** + * tricky lis + * https://www.youtube.com/watch?v=_eHbuLHo6pM&ab_channel=LearnCodeRepeat + * + * While you're iterating over all the elements from '0' to 'i-1', + * + * First check whether the addition of the current element will form a LIS or not. + * (This statements denotes to check condition of nums[i] > nums[j]) + * + * If a LIS is forming with the inclusion of element nums[i], then only a simple problem remains - Whether a subsequece of an equal length is already present or not. + * + * To resolve this problem, check whether you've already acheived a LIS of that length or not by simply comparing with the count[i]. + * + * If yes, the count all the subsequences that have been formed at index 'j' and add them to the subsequences formed without subsequences ending at nums[j]. + * (This statement denotes - dp[j] + 1 == dp[i]) + * + * If not, then count all the subsequences formed with the subsequences ending at nums[j] + * (This statement denotes - dp[j] + 1 > dp[i]) + * + * Arr => 1, 3, 5, 4, 7 + * dp => 1, 2, 3, 3, 4 + * cnt=> 1, 1, 1, 1, 2 when i at 7 and j at 4 dp[j] + 1 == dp[i] so count is 2 + */ +public class NumberOfLIS { + public int findNumberOfLIS(int[] nums) { + int n = nums.length; + //dp[i] will store the length of Longest Increasing Subsequence, ending at nums[i]. + int[] dp = new int[n]; + //count[i] will store the total number of Longest Increasing Subsequences, ending at nums[i]. + int[] count = new int[n]; + + Arrays.fill(dp,1); + Arrays.fill(count,1); + //lis : length of Longest Increasing Subsequence. + int lis = 0; + //res : total number of subsequences of length lis. + int res = 0; + for (int i = 1; i < n; i++) { + //Checking it's previous values. + for (int j = 0; j < i; j++) { + //If any previous value of nums[i] is less than it, then only it can be appended(as we know the + //subsequence would have ended at nums[j]). + if (nums[j] < nums[i]) { + //If dp[i] is equal to dp[j] + 1, meaning a different sequence has been found of same + //length, so increase count[i] by count[j]. + if (dp[i] == dp[j] + 1) { + count[i] += count[j]; + } + //Else, if dp[i] is less than dp[j] + 1, meaning length will increase as sequence will have a + //new element, so store dp[j] + 1 in dp[i] and count[j] in count[i]. + else if (dp[i] < dp[j] + 1) { + dp[i] = dp[j] + 1; + count[i] = count[j]; + } + } + } + + //If lis is equal to dp[i], meaning a new sequence is found of same length, so add count[i] in res. + if (lis == dp[i]) { + res += count[i]; + } + //Else if lis is less than dp[i], meaning a new sequence is formed of greater length, so store the + //new increased length in lis and count[i] in res. + else if (lis < dp[i]) { + lis = dp[i]; + res = count[i]; + } + } + + return res; + } +} diff --git a/src/main/java/dynamicProgramming/lcs/RussianDollEnvelope.java b/src/main/java/dynamicProgramming/lcs/RussianDollEnvelope.java new file mode 100644 index 0000000..318277e --- /dev/null +++ b/src/main/java/dynamicProgramming/lcs/RussianDollEnvelope.java @@ -0,0 +1,63 @@ +package dynamicProgramming.lcs; + +import java.util.Arrays; + +public class RussianDollEnvelope { + + public int maxEnvelopes(int[][] envelopes) { + + /** + * sort envelopes by width (envelopes[i][0]), then we only need to consider height + * //if two envelopes have same width, sort them by descending order + * //because [3, 4] cannot contains [3, 3], so we need to put [3, 4] before [3, 3] when sorting, + * //otherwise it will be counted as an increasing number if the order is [3, 3], [3, 4] + * //but we actually do not want to count them as valid russian doll envelopes + * + * prevent calculating the envelope with the same width? For example [3, 1] [3, 2] [3, 3] will get 3, but [3, 3], [3, 2], [3, 1] will get 1. + */ + Arrays.sort(envelopes, (a, b) -> { + if (a[0] == b[0]) return Integer.compare(b[1], a[1]); + return Integer.compare(a[0], b[0]); + }); + int result = 0; + + /** + * //KEY POINTS: after sorting them by width with increasing order, we need to find Longest Increasing Subsequence + * //by traversing height of each envelope, then we get the final result + * //store tails of each increasing subsequence with different length + * /*eg: 3, 5, 1, 8, 2, 12 + * * 1 + * * 1, 2 + * * 3, 5, 8 + * * 3, 5, 8, 12 + * * tails = {1, 2, 8, 12} + * + * //we do not care about what elements are in each subsequence, we only care about + * //tails of them, because every time we only compare with their tails to decide + * //which subsequence could we add new item and update the entire structure + */ + int[] increasingHeight = new int[envelopes.length]; + + for (int[] envelope : envelopes) { + + int left = 0; + int right = result; + while (left < right) { + + int mid = left + (right - left) / 2; + if (increasingHeight[mid] < envelope[1]) { + left = mid + 1; + } else { + right = mid; + } + } + + increasingHeight[left] = envelope[1]; + if (left == result) { + result++; + } + } + + return result; + } +} diff --git a/src/main/java/dynamicProgramming/lcs/ShortestCommonSupersequence.java b/src/main/java/dynamicProgramming/lcs/ShortestCommonSupersequence.java new file mode 100644 index 0000000..d608268 --- /dev/null +++ b/src/main/java/dynamicProgramming/lcs/ShortestCommonSupersequence.java @@ -0,0 +1,65 @@ +package dynamicProgramming.lcs; + +import java.util.Arrays; + +/** + * https://leetcode.com/problems/shortest-common-supersequence/ + *

+ *The idea is very simple. The result string should contain all characters of s1 and s2 discarding the common ones. + * -> S1+S2-LCS + * because characters appearing in LCS are coming twice in the result. So count them only once. + * + * O(MN) *O(String len) if we store string in DP + * else O(MN) + * + * Input: str1 = "abac", str2 = "cab" + * Output: "cabac" + * Explanation: + * str1 = "abac" is a subsequence of "cabac" because we can delete the first "c". + * str2 = "cab" is a subsequence of "cabac" because we can delete the last "ac". + * The answer provided is the shortest such string that satisfies these properties. + */ +public class ShortestCommonSupersequence { + + public String shortestCommonSuperSequence(String str1, String str2) { + + String lcs = longestCommonSubSeq(str1, str2); + int i = 0; + int j = 0; + + StringBuilder sb = new StringBuilder(); + for (char c : lcs.toCharArray()) { + while (i < str1.length() && str1.charAt(i) != c) sb.append(str1.charAt(i++)); + while (j < str2.length() && str2.charAt(j) != c) sb.append(str2.charAt(j++)); + sb.append(c); + i++; + j++; + } + sb.append(str1.substring(i)); + sb.append(str2.substring(j)); + + return sb.toString(); + } + + public static void main(String[] args) { + new ShortestCommonSupersequence().shortestCommonSuperSequence("abac","cab"); + } + + public String longestCommonSubSeq(String str1, String str2) { + String[][] dp = new String[str1.length() + 1][str2.length() + 1]; + for (String[] s : dp) { + Arrays.fill(s, ""); + } + for (int i = 1; i < dp.length; i++) { + for (int j = 1; j < dp[0].length; j++) { + if (str1.charAt(i - 1) == str2.charAt(j - 1)) { + dp[i][j] = dp[i - 1][j - 1] + str1.charAt(i - 1); + } else { + dp[i][j] = dp[i - 1][j].length() > dp[i][j - 1].length() ? dp[i - 1][j] : dp[i][j - 1]; + } + } + } + + return dp[str1.length()][str2.length()]; + } +} diff --git a/src/main/java/dynamicProgramming/lcs/TwoStringInterleavingToFormThird.java b/src/main/java/dynamicProgramming/lcs/TwoStringInterleavingToFormThird.java new file mode 100644 index 0000000..7a9abdf --- /dev/null +++ b/src/main/java/dynamicProgramming/lcs/TwoStringInterleavingToFormThird.java @@ -0,0 +1,71 @@ +package dynamicProgramming.lcs; + +import java.util.Arrays; + +/** + * http://www.geeksforgeeks.org/check-whether-a-given-string-is-an-interleaving-of-two-other-given-strings-set-2/ + */ +public class TwoStringInterleavingToFormThird { + + public boolean isInterleave(String s1, String s2, String s3) { + int m = s1.length(); + int n = s2.length(); + if (m + n != s3.length()) return false; + boolean[][] dp = new boolean[m + 1][n + 1]; + dp[0][0] = true; + for (int i = 1; i <= m; i++) { + if (dp[i - 1][0] && s1.charAt(i - 1) == s3.charAt(i - 1)) { + dp[i][0] = true; + } + } + for (int i = 1; i <= n; i++) { + if (dp[0][i - 1] && s2.charAt(i - 1) == s3.charAt(i - 1)) { + dp[0][i] = true; + } + } + for (int i = 1; i <= m; i++) { + for (int j = 1; j <= n; j++) { + if (s1.charAt(i - 1) == s3.charAt(i + j - 1) && dp[i - 1][j]) { + dp[i][j] = true; + } + if (s2.charAt(j - 1) == s3.charAt(i + j - 1) && dp[i][j - 1]) { + dp[i][j] = true; + } + } + } + return dp[m][n]; + } + + public static void main(String args[]) { + String str1 = "aab"; + String str2 = "axy"; + String str3 = "aaxaby"; + TwoStringInterleavingToFormThird sti = new TwoStringInterleavingToFormThird(); + System.out.println(sti.isInterleave(str1, str2, str3)); + } + + public boolean isInterleave(String s1, int i, String s2, int j, String s3, int k, Integer[][] memo) { + if (i == s1.length()) { + return s2.substring(j).equals(s3.substring(k)); + } + if (j == s2.length()) { + return s1.substring(i).equals(s3.substring(k)); + } + if (memo[i][j] !=null) { + return memo[i][j] == 1; + } + boolean ans = s3.charAt(k) == s1.charAt(i) && isInterleave(s1, i + 1, s2, j, s3, k + 1, memo) + || s3.charAt(k) == s2.charAt(j) && isInterleave(s1, i, s2, j + 1, s3, k + 1, memo); + + memo[i][j] = ans ? 1 : 0; + return ans; + } + public boolean isInterleaveRecursive(String s1, String s2, String s3) { + if (s1.length() + s2.length() != s3.length()) { + return false; + } + Integer[][] memo= new Integer[s1.length()][s2.length()]; + + return isInterleave(s1, 0, s2, 0, s3, 0, memo); + } +} diff --git a/src/main/java/dynamicProgramming/lcs/WildCardMatching.java b/src/main/java/dynamicProgramming/lcs/WildCardMatching.java new file mode 100644 index 0000000..b054e03 --- /dev/null +++ b/src/main/java/dynamicProgramming/lcs/WildCardMatching.java @@ -0,0 +1,123 @@ +package dynamicProgramming.lcs; + +/** + * General Idea: Credit: https://leetcode.com/problems/wildcard-matching/discuss/370736/Detailed-Intuition-From-Brute-force-to-Bottom-up-DP + * The idea is pretty straightforward : scan S and P while there is a match between the current character of S and the current character of P. + * If we reach the end of both strings while there is still a match, return True, otherwise return False. + * The scan is done by having a pointer in S and a pointer in P. + + Example: S="code" + The character 'c' of S matches the first character of P if the first character of P is: + + 'c' + '?' + '*' + + Case 1: + When the first character of P is a lowercase letter different from 'c', return False. + + Case 2: + If the first character of P is 'c' or '?', we move both pointers one step to the right. + + Case 3: + If the first character of P is '*', we have 2 possibilities: + + - '*' matches 0 character : in this case we move the pointer in P one step, ie will ignore the whole pattern + - '*' matches 1 or more characters : in this case we move the pointer in S one step, ie we consider pattern + And we continue like this for each two positions taken by the two pointers. + + - If we reach the end of P but there is still characters from S, simply return .. False ! + - If we reach the end of S and there is still characters from P, the only case when there is a match is that all the remaining characters in P are '*', + in this case these stars will be matched with the empty string. + **/ +public class WildCardMatching { + + public boolean isMatch(String s, String p) { + + return isMatchApproach1Helper(0, 0, s, p); + } + + public boolean isMatchApproach1Helper(int tIdx, int pIdx, String text, String pattern) { + + // reached the end of both S and P + if (tIdx == text.length() && pIdx == pattern.length()) { + return true; + } + // there are still characters in S => there is no match + else if (pIdx == pattern.length()) { + return false; // Can't have a non-empty s match an empty p. + } + // if we reached end of text and pattern is still left. + // Try to see if p at or after this stage is only * or ** or *** etc. Only way to match an empty text. + else if (tIdx == text.length()) { + return pattern.charAt(pIdx) == '*' && isMatchApproach1Helper(tIdx, pIdx + 1, text, pattern); + } + // Here cuz text & pattern match atleast a char + else if (text.charAt(tIdx) == pattern.charAt(pIdx) || pattern.charAt(pIdx) == '?') { + // Match here if strs from next index onward also are a match. Delegate job to recursive func for latter. + return isMatchApproach1Helper(tIdx + 1, pIdx + 1, text, pattern); + } + + // star either matches 0 or >=1 character + else if (pattern.charAt(pIdx) == '*') { + // 1: When * matches an empty seq, it's work is done. Hence, the next stage to check match for is w/o *. + // Also, there could be *s in line. So, consuming this *, could exhibit new p with next fresh *. + // 2: '*' can match seq of chars. Hence, * kept. Further rec stages could use it to match more chars/empty. + return isMatchApproach1Helper(tIdx, pIdx + 1, text, pattern) + || isMatchApproach1Helper(tIdx + 1, pIdx, text, pattern); + } + + return false; + } + + /******************* + * Approach 3: Bottom Up Tabulation: https://www.youtube.com/watch?v=3ZDZ-N0EPV0 + * Bottom-up the smallest is (0, 0) + * + * | dp[i-1][j-1] if str[i] == pattern[j] || pattern[j] == '?' + * | + * | if pattern[j-1] == '*' + * dp[i][j] = | dp[i][j-1] || dp[i-1][j] + * | + * | False + * + * Time Complexity : O(m * n) + * Space Complexity : O(m * n) + **********************************/ + public boolean isMatchBottomUp(String s, String t) { + //First, we need to create a 2d dp table dp. The size of this table is (s.size() + 1) * (p.size() + 1). + // We introduce +1 here to better handle the edge cases where we have an empty string or an empty pattern. + boolean[][] dp= new boolean[s.length()+1][t.length()+1]; + + //When both the string and the pattern are empty. + //Always match. dp[0][0] = true + dp[0][0]= true; + + for(int i=1;i<=t.length();i++){ + if(t.charAt(i-1)=='*') + dp[0][i]= dp[0][i-1]; + } + + for(int i=1;i<=s.length();i++){ + for(int j=1;j<=t.length();j++){ + + if(t.charAt(j-1)=='?' || s.charAt(i-1)==t.charAt(j-1)){ + dp[i][j]=dp[i-1][j-1]; + + }else if(t.charAt(j-1)=='*'){ + dp[i][j]= dp[i-1][j] || dp[i][j-1]; + }else{ + dp[i][j]=false; + } + } + } + return dp[s.length()][t.length()]; + } + + public static void main(String args[]) { + WildCardMatching wcm = new WildCardMatching(); + System.out.println(wcm.isMatch("xbylmz", "x?y***z")); + + } + +} \ No newline at end of file diff --git a/src/main/java/dynamicProgramming/matrix/MatrixMultiplicationCost.java b/src/main/java/dynamicProgramming/matrix/MatrixMultiplicationCost.java new file mode 100644 index 0000000..bd53dd9 --- /dev/null +++ b/src/main/java/dynamicProgramming/matrix/MatrixMultiplicationCost.java @@ -0,0 +1,43 @@ +package dynamicProgramming.matrix; + +/** + * http://www.geeksforgeeks.org/dynamic-programming-set-8-matrix-chain-multiplication/ + * https://www.youtube.com/watch?v=vgLJZMUfnsU&t=316s + */ +public class MatrixMultiplicationCost { + + + public static int matrixMultiplication(int[] arr, int N) { + Integer[][] dp = new Integer[N + 1][N + 1]; + return recursionHelper(arr, 1, N - 1, dp); + } + + public static int recursionHelper(int[] arr, int i, int j, Integer[][] dp) { + if (i == j) return 0; + if (dp[i][j] != null) return dp[i][j]; + int min = Integer.MAX_VALUE; +// Run a loop from 'i' to 'j' - 1 and calculate for all possible combination + for (int k = i; k < j; k++) { + min = Math.min(min, arr[i - 1] * arr[k] * arr[j] + recursionHelper(arr, i, k, dp) + recursionHelper(arr, k + 1, j, dp)); + } + + return dp[i][j] = min; + } + + public static int matrixMultiplicationTabulation(int[] arr, int N) { + int[][] dp = new int[N][N]; + + for (int i = N - 1; i > 0; i--) { + for (int j = i + 1; j < N; j++) { + + int min = Integer.MAX_VALUE; + for (int k = i; k < j; k++) { + min = Math.min(min, arr[i - 1] * arr[k] * arr[j] + dp[i][k] + dp[k + 1][j]); + } + dp[i][j] = min; + } + } + + return dp[1][N - 1]; + } +} \ No newline at end of file diff --git a/src/main/java/dynamicProgramming/matrix/MaximumSquareDP.java b/src/main/java/dynamicProgramming/matrix/MaximumSquareDP.java new file mode 100644 index 0000000..b5a4a1c --- /dev/null +++ b/src/main/java/dynamicProgramming/matrix/MaximumSquareDP.java @@ -0,0 +1,51 @@ +package dynamicProgramming.matrix; + +class MaximumSquareDP { + public int maximalSquare(char[][] matrix) { + int[][] dp = new int[matrix.length][matrix[0].length]; + int result = 0; + for (int i = 0; i < matrix[0].length; i++) { + dp[0][i] = matrix[0][i] == '1' ? 1 : 0; + result = Math.max(result, dp[0][i]); + } + for (int i = 0; i < matrix.length; i++) { + dp[i][0] = matrix[i][0] == '1' ? 1 : 0; + result = Math.max(result, dp[i][0]); + } + + for (int i = 1; i < matrix.length; i++) { + for (int j = 1; j < matrix[0].length; j++) { + if (matrix[i][j] == '0') continue; + + dp[i][j] = 1 + Math.min(dp[i - 1][j], Math.min(dp[i - 1][j - 1], dp[i][j - 1])); + result = Math.max(result, dp[i][j]); + } + } + + return result * result; + } + + public int maximalSquareRecursion(char[][] matrix) { + Integer[][] cache = new Integer[matrix.length][matrix[0].length]; + int result = 0; + for (int i = 0; i < matrix.length; i++) { + for (int j = 0; j < matrix[0].length; j++) { + if (matrix[i][j] == '1') { + result = Math.max(result, dfs(matrix, cache, i, j)); + } + } + } + + return result * result; + } + + public int dfs(char[][] matrix, Integer[][] cache, int i, int j) { + if (i >= matrix.length || j >= matrix[0].length || i < 0 || j < 0 || matrix[i][j] == '0') return 0; + + if (cache[i][j] != null) return cache[i][j]; + + cache[i][j] = 1 + Math.min(dfs(matrix, cache, i + 1, j), Math.min(dfs(matrix, cache, i + 1, j + 1), dfs(matrix, cache, i, j + 1))); + return cache[i][j]; + } +} + diff --git a/src/main/java/dynamicProgramming/matrix/MinCostPath.java b/src/main/java/dynamicProgramming/matrix/MinCostPath.java new file mode 100644 index 0000000..f1d04e1 --- /dev/null +++ b/src/main/java/dynamicProgramming/matrix/MinCostPath.java @@ -0,0 +1,52 @@ +package dynamicProgramming.matrix; + +/** + * http://www.geeksforgeeks.org/dynamic-programming-set-6-min-cost-path/ + */ +public class MinCostPath { + + public int minPathSum1(int[][] grid) { + + for(int i=1;i 0) { + min = Math.min(min, matrix[i - 1][j - 1]); + } + + if (j < n - 1) { + min = Math.min(min, matrix[i - 1][j + 1]); + } + + matrix[i][j] += min; + } + } + + return Arrays.stream(matrix[m - 1]).min().getAsInt(); + } + + public static void main(String[] args) { + minFallingPathSum(new int[][]{{2, 1, 3}, + {6, 5, 4}, + {7, 8, 9}}); + } +} diff --git a/src/main/java/dynamicProgramming/oiknapsack/EqualSubsetSumPartition.java b/src/main/java/dynamicProgramming/oiknapsack/EqualSubsetSumPartition.java new file mode 100644 index 0000000..e1676c6 --- /dev/null +++ b/src/main/java/dynamicProgramming/oiknapsack/EqualSubsetSumPartition.java @@ -0,0 +1,108 @@ +package dynamicProgramming.oiknapsack; + +import java.util.Arrays; + +/** + * https://www.educative.io/collection/page/5668639101419520/5633779737559040/5752754626625536 + *

+ * Given a non-empty array containing only positive integers, + * find if the array can be partitioned into two subsets such that the sum of elements in both subsets is equal. + *

+ * Input: [1, 5, 11, 5] + *

+ * Output: true + *

+ * Explanation: The array can be partitioned as [1, 5, 5] and [11]. + */ +class EqualSubsetSumPartition { + +// 0 1 2 3 4 5 +// +---+---+---+---+---+---+ +// {1} | T | T | F | F | F | F | +// +---+---+---+---+---+---+ +// {1,2} | T | T | T | T | F | F | +// +---+---+---+---+---+---+ +// {1,2,3} | T | T | T | T | T | T | +// +---+---+---+---+---+---+ +//{1,2,3,4} | T | T | T | T | T | T | +// +---+---+---+---+---+---+ + + public boolean canPartition(int[] nums) { + int totalSum = 0; + // find sum of all array elements + for (int num : nums) { + totalSum += num; + } + // if totalSum is odd, it cannot be partitioned into equal sum subset + if (totalSum % 2 != 0) return false; + int subSetSum = totalSum / 2; + int n = nums.length; + boolean dp[][] = new boolean[n + 1][subSetSum + 1]; + dp[0][0] = true; + for (int i = 1; i <= n; i++) { + int curr = nums[i - 1]; + for (int j = 0; j <= subSetSum; j++) { + if (j < curr) + dp[i][j] = dp[i - 1][j]; + else + dp[i][j] = dp[i - 1][j] || (dp[i - 1][j - curr]); + } + } + return dp[n][subSetSum]; + } + + Boolean[][] cache; + + public boolean canPartitionBottomUp(int[] nums) { + + int sum = 0; + for (int num : nums) { + sum += num; + } + if (sum % 2 == 1) return false; + int target = sum / 2; + cache = new Boolean[nums.length + 1][target + 1]; + return sumPossible(nums, 0, target); + } + + public boolean sumPossible(int[] nums, int i, int target) { + if (target < 0) return false; + if (target == 0) return true; + if (i >= nums.length) return false; + if (cache[i][target] != null) return cache[i][target]; + + return cache[i][target] = sumPossible(nums, i + 1, target - nums[i]) || sumPossible(nums, i + 1, target); + } + + public boolean canPartitionSpace(int[] nums) { + int sum = 0; + for (int i : nums) { + sum += i; + } + + if (sum % 2 == 1) return false; + int target = sum / 2; + boolean[] dp = new boolean[target + 1]; + dp[0] = true; + + for (int num : nums) { + for (int i = target; i > 0; i--) { + + // Go from behind to preserve data! + // We only need data from the previous number + // being looked at, that means the previous row. + // If we go from behind, that information is preserved. + + if (i - num >= 0) dp[i] = dp[i] || dp[i - num]; + } + } + + return dp[target]; + } + + public static void main(String[] args) { + EqualSubsetSumPartition ps = new EqualSubsetSumPartition(); + int[] num = {2, 3, 4, 5}; + System.out.println(ps.canPartition(num)); + } +} diff --git a/src/main/java/dynamicProgramming/oiknapsack/MinPartition.java b/src/main/java/dynamicProgramming/oiknapsack/MinPartition.java new file mode 100644 index 0000000..189cc40 --- /dev/null +++ b/src/main/java/dynamicProgramming/oiknapsack/MinPartition.java @@ -0,0 +1,46 @@ +package dynamicProgramming.oiknapsack; + +import java.util.HashMap; +import java.util.Map; + +class MinPartition +{ + + public static int minPartition(int[] S, int n, int S1, int S2, + Map lookup) + { + if (n < 0) { + return Math.abs(S1 - S2); + } + + // construct a unique map key from dynamic elements of the input + // Note that can uniquely identify the subproblem with n & S1 only, + // as S2 is nothing but S - S1 where S is sum of all elements + String key = n + "|" + S1; + + // if sub-problem is seen for the first time, solve it and + // store its result in a map + if (!lookup.containsKey(key)) + { + // Case 1. include current item in the subset S1 and recurse + // for remaining items (n - 1) + int inc = minPartition(S, n - 1, S1 + S[n], S2, lookup); + + // Case 2. exclude current item from subset S1 and recurse for + // remaining items (n - 1) + int exc = minPartition(S, n - 1, S1, S2 + S[n], lookup); + + lookup.put(key, Integer.min(inc, exc)); + } + + return lookup.get(key); + } + + public static void main(String[] args) + { + int[] S = { 10, 20, 15, 5, 25 }; + Map lookup = new HashMap<>(); + System.out.println("The minimum difference is " + + minPartition(S, S.length - 1, 0, 0, lookup)); + } +} \ No newline at end of file diff --git a/src/main/java/dynamicProgramming/oiknapsack/MinimumSubsetSum.java b/src/main/java/dynamicProgramming/oiknapsack/MinimumSubsetSum.java new file mode 100644 index 0000000..afbc06d --- /dev/null +++ b/src/main/java/dynamicProgramming/oiknapsack/MinimumSubsetSum.java @@ -0,0 +1,17 @@ +package dynamicProgramming.oiknapsack; + +/** + * Given a set of positive numbers, partition the set into two subsets with minimum difference between their subset sums. + * Input: {1, 2, 3, 9} + * Output: 3 + * Explanation: We can partition the given set into two subsets where minimum absolute difference + * between the sum of numbers is '3'. Following are the two subsets: {1, 2, 3} & {9}. + * + * Input: {1, 3, 100, 4} + * Output: 92 + * Explanation: We can partition the given set into two subsets where minimum absolute difference + * between the sum of numbers is '92'. Here are the two subsets: {1, 3, 4} & {100}. + */ +public class MinimumSubsetSum { + +} diff --git a/src/main/java/dynamicProgramming/oiknapsack/NumberOfUniqueWaysToMakeChange.java b/src/main/java/dynamicProgramming/oiknapsack/NumberOfUniqueWaysToMakeChange.java new file mode 100644 index 0000000..3c4db76 --- /dev/null +++ b/src/main/java/dynamicProgramming/oiknapsack/NumberOfUniqueWaysToMakeChange.java @@ -0,0 +1,34 @@ +package dynamicProgramming.oiknapsack; + +import java.util.Arrays; + +/* + * Java-solution-with-thinking-process-O(nm)-Time-and-O(m)-Space Note: This code + * shows the use of O(mn) space. This problem can be solved using only O(m) + * space. https://www.youtube.com/watch?v=DJ4a7cmjZY0 + */ +public class NumberOfUniqueWaysToMakeChange { + + public int numberOfSolutions(int total, int coins[]) { + int[][] temp = new int[coins.length + 1][total + 1]; + for (int i = 0; i <= coins.length; i++) { + temp[i][0] = 1; + } + for (int i = 1; i <= coins.length; i++) { + for (int j = 1; j <= total; j++) { + if (coins[i - 1] > j) { + temp[i][j] = temp[i - 1][j]; + } else { + temp[i][j] = temp[i][j - coins[i - 1]] + temp[i - 1][j]; + } + } + System.out.println(Arrays.toString(temp[i])); + } + return temp[coins.length][total]; + } + + public static void main(String[] args) { + int[] coins = { 1, 2, 5 }; + System.out.println(new NumberOfUniqueWaysToMakeChange().numberOfSolutions(5, coins)); + } +} diff --git a/src/main/java/dynamicProgramming/oiknapsack/O1KnapsackSpaceOptimized.java b/src/main/java/dynamicProgramming/oiknapsack/O1KnapsackSpaceOptimized.java new file mode 100644 index 0000000..13c4396 --- /dev/null +++ b/src/main/java/dynamicProgramming/oiknapsack/O1KnapsackSpaceOptimized.java @@ -0,0 +1,105 @@ +package dynamicProgramming.oiknapsack; + +class O1KnapsackSpaceOptimized { + + public static void main(String[] args) { + int wt[] = { 2, 3, 4, 5 }; + int val[] = { 1, 2, 4, 6 }; + int W = 7; + int n = 4; + System.out.println(findProfit(val, wt, W)); + } + + public static int findProfit(int[] val, int[] wt, int W) { + int[][] profits = new int[val.length + 1][W + 1]; + for (int i = 1; i <= val.length; i++) { + int currWeight = wt[i - 1]; + for (int j = 1; j <= W; j++) { +// So, for each item at index ‘i’ (0 <= i < items.length) and capacity ‘c’ (0 <= c <= capacity), we have two options: +// Include the item at index ‘i’ if its weight is not more than the capacity. +// In this case, we include its profit plus whatever profit we get from the remaining capacity +// and from remaining items => profit[i] + dp[i-1][c-weight[i]] + if (j - currWeight >= 0) { + profits[i][j] = Math.max(profits[i - 1][j], profits[i - 1][j - currWeight] + val[i - 1]); + } +// Exclude the item at index ‘i’. +// In this case, we will take whatever profit we get from the sub-array excluding this item => dp[i-1][c] + else { + profits[i][j] = profits[i - 1][j]; + } + } + } + return profits[val.length][W]; + } + +// 1) When we access dp[j], it has not been overridden yet for the current iteration, so it should be fine. +// 2) dp[j-weight[i]] might be overridden if “weight[i] > 0”. Therefore we can’t use this value for the current iteration. + +// To solve the second case, we can change our inner loop to process in the reverse direction: c:capacity-->0. +// This will ensure that whenever we change a value in dp[], we will not need it again in the current iteration. + + public int findProfitSpaceOptimised(int[] val, int[] wt, int W){ + int[] profits = new int[W + 1]; + // if we have only one weight we take if it's not more than the capacity + for (int c=0; c<=W;c++){ + if(wt[0]= 0; j--){ + int profit1 = 0, profit2=0; + if(wt[i-1]<=j){ + profit1= val[i-1]+profits[j-wt[i-1]]; + } + profit2=profits[j]; + profits[j]= Math.max(profit1,profit2); + } + } + return profits[W]; + } + +// Input: m = 10, A = [2, 3, 5, 7], V = [1, 5, 2, 4] +// Output: 9 +// Explanation: Put A[1] and A[3] into backpack, getting the maximum value V[1] + V[3] = 9 + + public int backPackII(int m, int[] A, int[] V) { + + int[][] dp= new int[A.length+1][m+1]; + + for(int i=1;i<=A.length;i++) { + for (int j = 1; j <= m; j++) { + if (A[i - 1] > m) { + dp[i][j] = dp[i - 1][j]; + } else { + int prevVal = j >= m ? dp[i - 1][j - m] : 0; + dp[i][j] = Math.max(dp[i - 1][j], + V[i - 1] + prevVal); + } + } + } + return dp[A.length][m]; + + } + + private static int unboundedKnapsack(int W, int n, + int[] val, int[] wt) + { + + // dp[i] is going to store maximum value + // with knapsack capacity i. + int dp[] = new int[W + 1]; + + // Fill dp[] using above recursive formula + for(int i = 0; i <= W; i++){ + for(int j = 0; j < n; j++){ + if(wt[j] <= i){ + dp[i] = Math.max(dp[i], dp[i - wt[j]] + + val[j]); + } + } + } + return dp[W]; + } + + +} \ No newline at end of file diff --git a/src/main/java/dynamicProgramming/oiknapsack/SubsetSumProblem.java b/src/main/java/dynamicProgramming/oiknapsack/SubsetSumProblem.java new file mode 100644 index 0000000..22b620a --- /dev/null +++ b/src/main/java/dynamicProgramming/oiknapsack/SubsetSumProblem.java @@ -0,0 +1,99 @@ +package dynamicProgramming.oiknapsack; + +import java.util.ArrayList; + +//Given a set of positive numbers, determine if a subset exists whose sum is equal to a given number ‘S’. + +/** + * Input: {1, 2, 3, 7}, S=6 + * Output: True + * The given set has a subset whose sum is '6': {1, 2, 3} + * + * Input: {1, 2, 7, 1, 5}, S=10 + * Output: True + * The given set has a subset whose sum is '10': {1, 2, 7} + */ + +// 0 1 2 3 4 5 6 +// +---+---+---+---+---+---+---+ +// {1} | T | T | F | F | F | F | F | +// +---+---+---+---+---+---+---+ +// {1,2} | T | T | T | T | F | F | F | +// +---+---+---+---+---+---+---+ +// {1,2,3} | T | T | T | T | T | T | T | +// +---+---+---+---+---+---+---+ +// {1,2,3,7} | T | T | T | T | T | T | T | +// +---+---+---+---+---+---+---+ +public class SubsetSumProblem { + + static boolean[][] dp; + + static void printAllSubsets(int arr[], int n, int sum) { + if (n == 0 || sum < 0) + return; + + dp = new boolean[n][sum + 1]; + for (int i = 0; i < n; ++i) { + dp[i][0] = true; + } + + if (arr[0] <= sum) + dp[0][arr[0]] = true; + + for (int i = 1; i < n; ++i) { + for (int j = 0; j <= sum; j++) { + if (arr[i] <= j) { + dp[i][j] = (dp[i - 1][j] || dp[i - 1][j - arr[i]]); + } else { + dp[i][j] = dp[i - 1][j]; + } + } + } + if (!dp[n - 1][sum]) { + System.out.println("There are no subsets with" + " sum " + sum); + return; + } + + ArrayList p = new ArrayList<>(); + } + + public boolean canPartition(int[] nums) { + if(nums==null || nums.length==0) return false; + + int sum=0; + for(int i: nums){ + sum+=i; + } + // odd numbers cannot be partitioned + if ((sum & 1) == 1) { + return false; + } + sum/=2; // checking for half of the the sum + boolean[][] dp= new boolean[nums.length+1][sum+1]; + + for(int i=0; i<=nums.length;i++){ + dp[i][0]= true; // first index of all rows are true because 0 is present in 0,0 + } + for(int i=1;i<=nums.length; i++){ + for(int j=1; j<=sum;j++){ + + if(nums[i-1]>j){ + dp[i][j]= dp[i-1][j]; + }else{ + dp[i][j]= (dp[i - 1][j] || dp[i-1][j-nums[i-1]]); + } + } + } + + + + return dp[nums.length][sum]; + } + + public static void main(String args[]) { + int arr[] = { 2, 3, 5, 8, 10 }; + int n = arr.length; + int sum = 10; + printAllSubsets(arr, n, sum); + } +} \ No newline at end of file diff --git a/src/main/java/dynamicProgramming/palindrome/CountSubStrings.java b/src/main/java/dynamicProgramming/palindrome/CountSubStrings.java new file mode 100644 index 0000000..17f918f --- /dev/null +++ b/src/main/java/dynamicProgramming/palindrome/CountSubStrings.java @@ -0,0 +1,57 @@ +package dynamicProgramming.palindrome; + +public class CountSubStrings { + public int countSubstrings(String s) { + int count = 0; + for (int i = 0; i < s.length(); i++) + count += countSubstrings(s, i, i) + countSubstrings(s, i, i + 1); + return count; + } + + private int countSubstrings(String s, int start, int end) { + int count = 0; + while (start >= 0 && end < s.length() && s.charAt(start--) == s.charAt(end++)) + count++; + return count; + } + + public static int countSubstring(String s) { + int n = s.length(); + if (n < 2) + return n; + int count = n; + boolean[][] dp = new boolean[n][n]; + + // size 1 substrings are palindromes + for (int i = 0; i < n; i++) + dp[i][i] = true; + + // for size 2 substrings, check first and last char + for (int i = 0; i + 1 < n; i++) + if (s.charAt(i) == s.charAt(i + 1)) { + dp[i][i + 1] = true; + count++; + } + + // for size = 3+ + for (int len = 2; len < n; len++) // controls the size of the substring + for (int i = 0; i + len < n; i++) { // controls the start index + int j = i + len; // end index + System.out.print(i + " - " + j + " "); + // if s.charAt(i) == s.charAt(j) means the substring's first and last are equal letters + // we don't have to check the contents, we just have to check if lower diagonal is true + // lower diagonal is the result of the content in between s.charAt(i) == s.charAt(j) + + if ((s.charAt(i) == s.charAt(j)) && dp[i + 1][j - 1]) { + dp[i][j] = true; + count++; + } + System.out.println(); + } + return count; + } + + public static void main(String[] args) { + System.out.println(countSubstring("aaaa")); + } +} diff --git a/src/main/java/dynamicProgramming/palindrome/LongestPalindromicSubsequence.java b/src/main/java/dynamicProgramming/palindrome/LongestPalindromicSubsequence.java new file mode 100644 index 0000000..cadc967 --- /dev/null +++ b/src/main/java/dynamicProgramming/palindrome/LongestPalindromicSubsequence.java @@ -0,0 +1,86 @@ +package dynamicProgramming.palindrome; + +import java.util.Arrays; + +/** + * Date 08/01/2014 + * + * @author Tushar Roy + * + * Time complexity - O(n2) Space complexity - O(n2) + * + * Youtube link - https://youtu.be/_nCsPn7_OgI + * + * References + * http://www.geeksforgeeks.org/dynamic-programming-set-12-longest-palindromic-subsequence/ + */ +public class LongestPalindromicSubsequence { + + public int calculate1(char[] str) { + int[][] T = new int[str.length][str.length]; + for (int i = 0; i < str.length; i++) { + T[i][i] = 1; + } + for (int l = 2; l < str.length; l++) { + for (int i = 0; i+l < str.length ; i++) { + int j = i + l; + if (l == 2 && str[i] == str[j]) { + T[i][j] = 2; + } else if (str[i] == str[j]) { + T[i][j] = T[i + 1][j - 1] + 2; + } else { + T[i][j] = Math.max(T[i + 1][j], T[i][j - 1]); + } + } + } + return T[0][str.length - 1]; + } + + public int longestPalindromeSubseq(String s) { + char[] chars = s.toCharArray(); + int n = s.length(); + int[][] dp = new int[n][n]; + for (int i = n - 1; i >= 0; i--) { + dp[i][i] = 1; + for (int j = i + 1; j < n; ++j) { + if (chars[i] == chars[j]) { + dp[i][j] = dp[i + 1][j - 1] + 2; + } else { + dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]); + } + } + } + for (int[] ints : dp) { + System.out.println(Arrays.toString(ints)); + } + return dp[0][n - 1]; + + } + + public int longestPalindromeSubseqRecusive(String s) { + return helper(s, 0, s.length() - 1, new Integer[s.length()][s.length()]); + } + + private int helper(String s, int i, int j, Integer[][] memo) { + if (memo[i][j] != null) { + return memo[i][j]; + } + if (i > j) return 0; + if (i == j) return 1; + + if (s.charAt(i) == s.charAt(j)) { + memo[i][j] = helper(s, i + 1, j - 1, memo) + 2; + } else { + memo[i][j] = Math.max(helper(s, i + 1, j, memo), helper(s, i, j - 1, memo)); + } + return memo[i][j]; + } + + public static void main(String args[]) { + LongestPalindromicSubsequence lps = new LongestPalindromicSubsequence(); + String str = "agbdba"; + int r2 = lps.longestPalindromeSubseq(str); + System.out.print(r2); + } + +} \ No newline at end of file diff --git a/src/main/java/dynamicProgramming/palindrome/LongestPalindromicSubstring.java b/src/main/java/dynamicProgramming/palindrome/LongestPalindromicSubstring.java new file mode 100644 index 0000000..b2a3e07 --- /dev/null +++ b/src/main/java/dynamicProgramming/palindrome/LongestPalindromicSubstring.java @@ -0,0 +1,45 @@ +package dynamicProgramming.palindrome; + +/** + * https://leetcode.com/problems/longest-palindromic-substring/ + */ +public class LongestPalindromicSubstring { + + public static String longestPalindrome(String s) { + int len = s.length(); + if (len < 2) + return s; + int[] maxStart = new int[1], maxEnd = new int[1]; + // odd length ababc => here we start j and k at same position say index 2 and go left and right + // even length cbbd=> here let's say we're at index 1, we need to take 1 and 2 index to check for palindrome + // the above cases are the reason for sending i and i+1 + for (int i = 0; i < s.length() - 1; i++) { + extend(s, i, i, maxStart, maxEnd); + extend(s, i, i + 1, maxStart, maxEnd); + } + + return s.substring(maxStart[0], maxEnd[0] + 1); + } + + private static void extend(String s, int i, int j, int[] maxStart, int[] maxEnd) { + // loop until meet invalid match + while (i >= 0 && j < s.length() && s.charAt(i) == s.charAt(j)) { + i--; + j++; + } + + i++; + j--; // back to the last valid match + + if (j - i + 1 > maxEnd[0] - maxStart[0] + 1) { + maxStart[0] = i; + maxEnd[0] = j; + } + } + + + public static void main(String args[]) { + + System.out.println(longestPalindrome("bananas")); + } +} \ No newline at end of file diff --git a/src/main/java/dynamicProgramming/palindrome/PalindromePartitioning.java b/src/main/java/dynamicProgramming/palindrome/PalindromePartitioning.java new file mode 100644 index 0000000..3280e12 --- /dev/null +++ b/src/main/java/dynamicProgramming/palindrome/PalindromePartitioning.java @@ -0,0 +1,65 @@ +package dynamicProgramming.palindrome; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * https://leetcode.com/problems/palindrome-partitioning/ + * The aim to partition the string into all possible palindrome combinations. + * To achieve this, we must generate all possible substrings of a string by partitioning at every index + * until we reach the end of the string. + * Example, abba can be partitioned as ["a","ab","abb","abba"]. + * Each generated substring is considered as a potential candidate if it's a Palindrome + */ +class PalindromePartitioning { + + public List> partition(String s) { + if (s == null || s.length() == 0) { + return Collections.emptyList(); + } + + List> result = new ArrayList<>(); + backtrackingUtil(s, result, new ArrayList<>(), 0); + + return result; + } + + public void backtrackingUtil(String s, List> result, List tempList, int start) { + if (start >= s.length()) { + result.add(new ArrayList<>(tempList)); + return; + } + + for (int i = start; i < s.length(); i++) { + String temp = s.substring(start, i + 1); + if (isPalindrome(temp)) { + tempList.add(temp); + backtrackingUtil(s, result, tempList, i + 1); + tempList.remove(tempList.size() - 1); + } + } + } + + public boolean isPalindrome(String s) { + int start = 0; + int end = s.length() - 1; + while (start <= end) { + if (s.charAt(start) != s.charAt(end)) { + return false; + } + start++; + end--; + } + return true; + } + + public static void main(String[] args) { + PalindromePartitioning partitioning = new PalindromePartitioning(); + partitioning.partition("aab"); + } +} + + + + diff --git a/src/main/java/dynamicProgramming/palindrome/PalindromePartitioningII.java b/src/main/java/dynamicProgramming/palindrome/PalindromePartitioningII.java new file mode 100644 index 0000000..d227e5e --- /dev/null +++ b/src/main/java/dynamicProgramming/palindrome/PalindromePartitioningII.java @@ -0,0 +1,111 @@ +package dynamicProgramming.palindrome; + +/** + * revise + * https://leetcode.com/problems/palindrome-partitioning-ii + */ +public class PalindromePartitioningII { + + public static int minCutPalindromicSubstringVariant(String s) { + int[] cutsDp = new int[s.length()]; + for (int i = 1; i < s.length(); i++) { + cutsDp[i] = i; + } + + for (int mid = 0; mid < s.length(); mid++) { + findMin(mid, mid, cutsDp, s); + findMin(mid, mid + 1, cutsDp, s); + } + + return cutsDp[s.length() - 1]; + } + + public static void findMin(int start, int end, int[] cutsDp, String s) { + for (int i = start, j = end; i >= 0 && j < s.length() && s.charAt(i) == s.charAt(j); i--, j++) { + int newCut = i == 0 ? 0 : cutsDp[i - 1] + 1; + cutsDp[j] = Math.min(cutsDp[j], newCut); + } + } + + public static void main(String[] args) { + minCutPalindromicSubstringVariant("nooradars"); + } + + public int minCut(String s) { + int n, min; + n = s.length(); + + //cut[i] represents minimum number of cuts from String 0 to i + int[] cut = new int[n]; + + //p[i][j] represents String i to j is a palindrome or not + boolean[][] p = new boolean[n][n]; + + for (int i = 0; i < n; i++) { + min = i; // Max number of cuts is i for string length i+1 + for (int j = 0; j <= i; j++) { + // Why i - j < 3 ? + // 1. String of length 1 is always palindrome so no need to check in boolean table + // 2. String of length 2 is palindrome if Ci == Cj which is already checked in first part so no need to check again + // 3. String of length 3 is palindrome if Ci == Cj which is already checked in first part and Ci+1 and Cj-1 is same character which is always a palindrome + + // If String length >=4 + // then check if Ci == Cj and if they are equal check if String[j+1 .. i-1] is a palindrome from the boolean table + /** + * a b a | c c + * j i + * j-1 | [j, i] is palindrome + * cut(j-1) + 1 + */ + if (s.charAt(j) == s.charAt(i) && (i - j < 3 || p[j + 1][i - 1])) { + // Its a palindrome as Ci == Cj and String[j+1...i-1] is a palindrome + p[j][i] = true; + // j == 0 because String from j to i is a palindrome and it starts from first character so means no cuts needed + // Else I need a cut at jth location and it will be cuts encountered till j-1 + 1 + min = j == 0 ? 0 : Math.min(min, cut[j - 1] + 1); + } + } + cut[i] = min; + } + return cut[n - 1]; + } + + + Integer[][] cache; + + public int minCutRecursive(String s) { + cache = new Integer[s.length() + 1][s.length() + 1]; + + return recursionHelper(s, 0, s.length() - 1); + } + + public int recursionHelper(String s, int start, int end) { + if (start == end || isPalin(s, start, end)) { + return 0; + } + if (cache[start][end] != null) return cache[start][end]; + + int minCuts = end - start; + for (int i = start; i <= end; i++) { + if (isPalin(s, start, i)) { + minCuts = Math.min(minCuts, 1 + recursionHelper(s, i + 1, end)); + } + } + + return cache[start][end] = minCuts; + } + + public boolean isPalin(String s, int start, int end) { + if (start > end) return false; + + while (start < end) { + if (s.charAt(start) != s.charAt(end)) return false; + + start++; + end--; + } + + return true; + + } +} diff --git a/src/main/java/dynamicProgramming/stocks/BuyAndSellStockAnytime.java b/src/main/java/dynamicProgramming/stocks/BuyAndSellStockAnytime.java new file mode 100644 index 0000000..c2388f8 --- /dev/null +++ b/src/main/java/dynamicProgramming/stocks/BuyAndSellStockAnytime.java @@ -0,0 +1,29 @@ +package dynamicProgramming.stocks; + +import java.util.ArrayList; +import java.util.List; + +/** + * https://leetcode.com/problems/best-time-to-buy-and-sell-stock-ii/ + */ +public class BuyAndSellStockAnytime { + + public int maxProfit(int[] prices) { + int total = 0; + for (int i = 0; i < prices.length - 1; i++) { + if (prices[i + 1] > prices[i]) { + total += prices[i + 1] - prices[i]; + System.out.println(prices[i + 1] +" - "+ prices[i] +" = "+(prices[i + 1] - prices[i])); + } + } + return total; + } + + + + public static void main(String[] args) { + BuyAndSellStockAnytime stock = new BuyAndSellStockAnytime(); + int[] arr = {200, 180, 260, 310, 40, 535, 695}; + System.out.println(stock.maxProfit(arr)); + } +} diff --git a/src/main/java/dynamicProgramming/stocks/BuyAndSellStockAtMostTwice.java b/src/main/java/dynamicProgramming/stocks/BuyAndSellStockAtMostTwice.java new file mode 100644 index 0000000..5d19e78 --- /dev/null +++ b/src/main/java/dynamicProgramming/stocks/BuyAndSellStockAtMostTwice.java @@ -0,0 +1,74 @@ +package dynamicProgramming.stocks; + +/** + * https://leetcode.com/problems/best-time-to-buy-and-sell-stock-iii/ + */ +class BuyAndSellStockAtMostTwice { + + public static void main(String args[]) { + System.out.println("Maximum Profit = " + maxProfit(new int[]{2, 30, 15, 10, 8, 25, 80})); + } + + /** + * the idea is when we find a profit which is from + * i to n, we can break it in to i to k, k+1 to n + * in this manner at each point we can calculate profit from + * (left min element to current element) and (current element to right max element) + * the second part of the above eq can be achieved by coming from right to left + * for input [3,3,5,0,0,3,1,4] + * profit from l->r [0,0,2,2,2,3,3,4] + * profit from r->l [4,4,4,4,4,3,3,0] + * this simply states that at index 2 if we come from left the profit is 2 + * and we can initiate another transaction to obtain another profit + */ + public static int maxProfit(int[] prices) { + int[] profit1 = new int[prices.length]; + int[] profit2 = new int[prices.length]; + + int min = prices[0]; + for (int i = 1; i < prices.length; i++) { + min = Math.min(min, prices[i]); + profit1[i] = Math.max(profit1[i - 1], prices[i] - min); + } + + + int max = prices[prices.length - 1]; + + for (int i = prices.length - 2; i >= 0; i--) { + max = Math.max(max, prices[i]); + profit2[i] = Math.max(profit2[i + 1], max - prices[i]); + } + //at any pos 'i' profit1[i] denotes profit upto 0 to i + // at any pos 'i' profit2[i] denotes profit upto i+1 to n + int result = 0; + for (int i = 0; i < prices.length; i++) { + result = Math.max(result, profit2[i] + profit1[i]); + } + return result; + } + + /** + * Buy sell recursive template + */ + public int maxProfitRecursive(int[] prices) { + Integer[][][] dp = new Integer[prices.length + 1][2][3]; + return recursionHelper(prices, 0, 0, 2, dp); + } + + public int recursionHelper(int[] prices, int idx, int canSell, int txn, Integer[][][] dp) { + + if (txn == 0) return 0; + if (idx >= prices.length) return 0; + if (dp[idx][canSell][txn] != null) return dp[idx][canSell][txn]; + if (canSell == 0) { + int buy = -prices[idx] + recursionHelper(prices, idx + 1, 1, txn, dp); + int notBuy = recursionHelper(prices, idx + 1, 0, txn, dp); + return dp[idx][canSell][txn] = Math.max(buy, notBuy); + } else { + int sell = prices[idx] + recursionHelper(prices, idx + 1, 0, txn - 1, dp); + int notSell = recursionHelper(prices, idx + 1, 1, txn, dp); + return dp[idx][canSell][txn] = Math.max(sell, notSell); + } + } + +} diff --git a/src/main/java/dynamicProgramming/stocks/BuyAndSellWithTransactionFee.java b/src/main/java/dynamicProgramming/stocks/BuyAndSellWithTransactionFee.java new file mode 100644 index 0000000..109229e --- /dev/null +++ b/src/main/java/dynamicProgramming/stocks/BuyAndSellWithTransactionFee.java @@ -0,0 +1,48 @@ +package dynamicProgramming.stocks; + +/** + * https://leetcode.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee + */ +public class BuyAndSellWithTransactionFee { + + public int maxProfit(int[] prices, int fee) { + if (prices == null || prices.length == 0) { + return 0; + } + + int days = prices.length; + int[] buy = new int[days]; // the max profit in ith day when the last operation is buy + int[] sell = new int[days]; // the max profit in ith day when the last operation is sell + + buy[0] = -prices[0]; + + for (int i = 1; i < days; i++) { + buy[i] = Math.max(buy[i - 1], sell[i - 1] - prices[i]); + sell[i] = Math.max(sell[i - 1], buy[i - 1] + prices[i] - fee); + } + + return sell[days - 1]; + } + + public int maxProfitRecursive(int[] prices, int fee) { + Integer[][] dp = new Integer[prices.length + 1][2]; + return recursionHelper(prices, 0, 0, fee, dp); + } + + public int recursionHelper(int[] prices, int idx, int canSell, int fee, Integer[][] dp) { + + if (idx >= prices.length) return 0; + if (dp[idx][canSell] != null) return dp[idx][canSell]; + if (canSell == 0) { + + int buy = -prices[idx] + recursionHelper(prices, idx + 1, 1, fee, dp); + int notBuy = recursionHelper(prices, idx + 1, 0, fee, dp); + return dp[idx][canSell] = Math.max(buy, notBuy); + } else { + int sell = prices[idx] + recursionHelper(prices, idx + 1, 0, fee, dp) - fee; + int notSell = recursionHelper(prices, idx + 1, 1, fee, dp); + return dp[idx][canSell] = Math.max(sell, notSell); + } + } + +} diff --git a/src/main/java/dynamicProgramming/stocks/StockBuySellKTransactions.java b/src/main/java/dynamicProgramming/stocks/StockBuySellKTransactions.java new file mode 100644 index 0000000..c3d956b --- /dev/null +++ b/src/main/java/dynamicProgramming/stocks/StockBuySellKTransactions.java @@ -0,0 +1,157 @@ +package dynamicProgramming.stocks; + +import java.util.Arrays; +import java.util.Deque; +import java.util.LinkedList; + +/** + * Date 12/22/2015 + * + * @author Tushar Roy + *

+ * Time complexity - O(number of transactions * number of days) Space + * complexity - O(number of transactions * number of days) + *

+ * https://leetcode.com/discuss/15153/a-clean-dp-solution-which-generalizes-to-k-transactions + * https://www.youtube.com/watch?v=Pw6lrYANjz4&t=1228s + */ +public class StockBuySellKTransactions { + + public int maxProfit(int prices[], int K) { + if (K == 0 || prices.length == 0) { + return 0; + } + int[][] T = new int[K + 1][prices.length]; + + for (int i = 1; i < T.length; i++) { + int maxDiff = -prices[0]; + for (int j = 1; j < T[0].length; j++) { + T[i][j] = Math.max(T[i][j - 1], prices[j] + maxDiff); + maxDiff = Math.max(maxDiff, T[i - 1][j] - prices[j]); + } + } + + System.out.println(Arrays.deepToString(T)); + printActualSolution(T, prices); + return T[K][prices.length - 1]; + } + + public int maxProfitSpaceEfficient(int k, int[] prices) { + int n = prices.length; + if (k >= n / 2) { //if k >= n/2, then you can make maximum number of transactions + int maxProfit = 0; + for (int i = 1; i < n; i++) { + if (prices[i] > prices[i - 1]) { + maxProfit += prices[i] - prices[i - 1]; + } + } + return maxProfit; + } + int[] buy = new int[k + 1], sell = new int[k + 1]; + Arrays.fill(buy, Integer.MIN_VALUE); + for (int price : prices) { + for (int i = 1; i <= k; i++) { + buy[i] = Math.max(buy[i], sell[i - 1] - price); + sell[i] = Math.max(sell[i], buy[i] + price); + } + } + return sell[k]; + } + + public int maxProfit(int k, int[] prices) { + int[][] dp = new int[k + 1][prices.length + 1]; + + int n = prices.length; + if (n <= 1) { + return 0; + } + if (k >= n / 2) { //if k >= n/2, then you can make maximum number of transactions + int maxProfit = 0; + for (int i = 1; i < n; i++) { + if (prices[i] > prices[i - 1]) { + maxProfit += prices[i] - prices[i - 1]; + } + } + return maxProfit; + } + + for (int i = 1; i <= k; i++) { + for (int priceDay = 1; priceDay <= prices.length; priceDay++) { + + int notTransactingAtPriceDay = dp[i][priceDay - 1]; + + for (int m = 0; m < priceDay; m++) { + int transactionTillPriceDay = prices[priceDay - 1] - prices[m]; //[priceDay-1] because priceday begins at 1 + /* + * previous transaction till m, + * because if we are buy at m '(prices[priceDay - 1] - prices[m])', + * we should've completed transaction till m 'dp[i - 1][m]' + */ + int previousTransactionTillM = dp[i - 1][m]; + int temp = Math.max(notTransactingAtPriceDay, transactionTillPriceDay + previousTransactionTillM); + dp[i][priceDay] = Math.max(dp[i][priceDay], temp); + } + } + } + return dp[k][prices.length]; + } + + + public void printActualSolution(int T[][], int prices[]) { + int i = T.length - 1; + int j = T[0].length - 1; + + Deque stack = new LinkedList<>(); + while (true) { + if (i == 0 || j == 0) { + break; + } + if (T[i][j] == T[i][j - 1]) { + j = j - 1; + } else { + stack.addFirst(j); + int maxDiff = T[i][j] - prices[j]; + for (int k = j - 1; k >= 0; k--) { + if (T[i - 1][k] - prices[k] == maxDiff) { + i = i - 1; + j = k; + stack.addFirst(j); + break; + } + } + } + } + + while (!stack.isEmpty()) { + System.out.println("Buy at price " + prices[stack.pollFirst()]); + System.out.println("Sell at price " + prices[stack.pollFirst()]); + } + + } + public int maxProfitRecursive(int k, int[] prices) { + Integer[][][] dp = new Integer[prices.length+1][2][k+1]; + return recursionHelper(prices,0,0,k,dp); + } + + public int recursionHelper(int[] prices, int idx,int canSell, int txn,Integer[][][] dp){ + if(idx>=prices.length || txn==0 ) return 0; + if(dp[idx][canSell][txn]!=null) return dp[idx][canSell][txn]; + if(canSell==0){ + + int buy = -prices[idx]+recursionHelper(prices,idx+1,1,txn,dp); + int notBuy = recursionHelper(prices,idx+1,0,txn,dp); + return dp[idx][canSell][txn]=Math.max(buy,notBuy); + }else{ + int sell = prices[idx]+recursionHelper(prices,idx+1,0,txn-1,dp); + int notSell = recursionHelper(prices,idx+1,1,txn,dp); + return dp[idx][canSell][txn]=Math.max(sell,notSell); + } + } + + public static void main(String args[]) { + StockBuySellKTransactions sbt = new StockBuySellKTransactions(); + int prices[] = {2, 5, 7, 1, 4, 3, 1, 3}; + + System.out.println("Max profit fast solution " + sbt.maxProfit(prices, 3)); + } +} diff --git a/src/main/java/dynamicProgramming/stocks/StockBuySellWithCoolDown.java b/src/main/java/dynamicProgramming/stocks/StockBuySellWithCoolDown.java new file mode 100644 index 0000000..2e3595e --- /dev/null +++ b/src/main/java/dynamicProgramming/stocks/StockBuySellWithCoolDown.java @@ -0,0 +1,82 @@ +package dynamicProgramming.stocks; + +public class StockBuySellWithCoolDown { + + public int maxProfit(int[] prices) { + + if (prices == null || prices.length < 2) return 0; + int buy = 0, sell = -prices[0], rest = 0; + + // Assume the buy, sell and rest are states + // the transistions would be + // 1) from Rest you have to come to buy + // 2) from buy you can rest/hold or you can sell + // 3) from sell you can hold or sell and go to Rest + + // state 1=> first transistion max(buy, rest) we can either buy or rest at this point + // state 2=> we can either hold what was there in previous state or buy so '-' price[i] + // state 3=> to come to rest we have to sell and make profit so only the '+' sign + + for (int i = 1; i < prices.length; i++) { + int tmp = buy; + buy = Math.max(buy, rest); + rest = sell + prices[i]; + sell = Math.max(sell, tmp - prices[i]); + } + return Math.max(buy, rest); + } + + /** + * cooldown[i] = max(cooldown[i - 1], sell[i - 1]); // Stay at cooldown, or rest from sell + * proceed to buy, ie, we have no stock now, and the max profit should be ''last no stock profit'' or ''last rest profit'' + *

+ * buy[i] = max(buy[i - 1], cooldown[i - 1] - prices[i]); // Stay at buy, or buy from cooldown + * //can proceed to sell, ie, we now have stock, and the profit should be ''last stock profit'' or ''last no stock but buy this time'' + *

+ * sell[i] = buy[i - 1] + prices[i]; // Only one way from s1 + * //we should sell then take a rest + */ + public int maxProfitExtraSpace(int[] prices) { + + int n = prices.length; + int[] buy = new int[prices.length + 1]; + int[] sell = new int[prices.length + 1]; + + buy[1] = -prices[0]; + sell[1] = 0; + if (n == 1) { + return sell[0]; + } + + for (int i = 2; i <= n; i++) { + buy[i] = Math.max(buy[i - 1], sell[i - 2] - prices[i - 1]); + + sell[i] = Math.max(sell[i - 1], buy[i - 1] + prices[i - 1]); + } + return sell[n]; + + } + + public static void main(String[] args) { + new StockBuySellWithCoolDown().maxProfitExtraSpace(new int[]{1, 2, 3, 0, 2}); + } + + public int maxProfitRecursive(int[] prices) { + Integer[][] dp = new Integer[prices.length][2]; + return recursionHelper(prices, 0, 0, dp); + } + + public int recursionHelper(int[] prices, int idx, int canSell, Integer[][] dp) { + if (idx >= prices.length) return 0; + if (dp[idx][canSell] != null) return dp[idx][canSell]; + if (canSell == 0) { + int buy = -prices[idx] + recursionHelper(prices, idx + 1, 1, dp); + int notBuy = recursionHelper(prices, idx + 1, 0, dp); + return dp[idx][canSell] = Math.max(buy, notBuy); + } else { + int sell = prices[idx] + recursionHelper(prices, idx + 2, 0, dp); + int notSell = recursionHelper(prices, idx + 1, 1, dp); + return dp[idx][canSell] = Math.max(sell, notSell); + } + } +} diff --git a/src/main/java/dynamicProgramming/unboundedknapsack/CuttingRod.java b/src/main/java/dynamicProgramming/unboundedknapsack/CuttingRod.java new file mode 100644 index 0000000..3b3b8e9 --- /dev/null +++ b/src/main/java/dynamicProgramming/unboundedknapsack/CuttingRod.java @@ -0,0 +1,80 @@ +package dynamicProgramming.unboundedknapsack; + +import java.util.Arrays; + +/** + * https://leetcode.com/problems/minimum-cost-to-cut-a-stick + * https://www.youtube.com/watch?v=xwomavsC86c + */ +public class CuttingRod { + + public int minCost(int n, int[] cuts) { + int len = cuts.length + 2; + + int[] endpoints = new int[len]; + endpoints[0] = 0; + for (int i = 1; i < len - 1; i++) endpoints[i] = cuts[i - 1]; + endpoints[len - 1] = n; + Arrays.sort(endpoints); + + + int[][] dp = new int[len][len]; + + // d : dist between i & j, the starting & ending position of stick + for (int d = 2; d < len; d++) { + for (int i = 0, j = i + d; j < len; i++, j++) { + dp[i][j] = Integer.MAX_VALUE; + + int curr = endpoints[j] - endpoints[i]; + for (int k = i + 1; k < j; k++) { + dp[i][j] = Math.min(dp[i][j], dp[i][k] + dp[k][j] + curr); + } + } + } + return dp[0][len - 1]; + } + + /** + * The algorithm is quite Brute Force, we would try to generate all possible permutations of cuts, + * and would try to know what permutation would lead to best result, i.e. minimize our cost for cutting. + *

+ * Let us suppose we are currently having a wood piece from index l to index r (i.e. the length of the wood is r - l, indexing is done as illustrated in the problem). + * Now, we try every possible cut that we could perform in the range from l to r. + *

+ * Since a cut (let's say, cut is at i index) results in our original piece to further split into 2 parts (one from [l, i], and second from [i, r]). + * Also, lets suppose the minimum cost of cutting, the segment [l, i] is minLeft and similarly for [i, r] is minRight. + * Hence the cost to cut the rod segment [l, r] would be cost_i = minLeft + minRight + (r - l) (r - l is the cost to perform the cut at i). + * Similarly, a cut at j index would cost in total, say, cost_j, similarly at k be cost_k and so on... + *

+ * The minimum cost to cut the rod from index l to r hence would be min(cost_i, cost_j, cost_k, ...). + */ + public int minCostRecursive(int n, int[] cuts) { + Integer[][] dp = new Integer[101][101]; + int[] cutsWithLengthOfRodAppendedToEnds = Arrays.copyOf(cuts, cuts.length + 2); + cutsWithLengthOfRodAppendedToEnds[cutsWithLengthOfRodAppendedToEnds.length - 1] = n; + Arrays.sort(cutsWithLengthOfRodAppendedToEnds); + return recursionHelper(cutsWithLengthOfRodAppendedToEnds, 1, cutsWithLengthOfRodAppendedToEnds.length - 2, dp); + } + + public int recursionHelper(int[] cutsWithLengthOfRod, int i, int j, Integer[][] dp) { + if (i > j) return 0; + if (dp[i][j] != null) return dp[i][j]; + int min = Integer.MAX_VALUE; + + for (int mid = i; mid <= j; mid++) { + + int cost = cutsWithLengthOfRod[j + 1] - cutsWithLengthOfRod[i - 1] + recursionHelper(cutsWithLengthOfRod, i, mid - 1, dp) + + recursionHelper(cutsWithLengthOfRod, mid + 1, j, dp); + min = Math.min(cost, min); + } + + return dp[i][j] = min; + + } + + public static void main(String args[]) { + CuttingRod cr = new CuttingRod(); + int[] price = {1, 5, 3, 6}; + System.out.println(cr.minCost(9, price)); + } +} \ No newline at end of file diff --git a/src/geeksforgeeks/ArticulationPoint.java b/src/main/java/graph/ArticulationPoint.java similarity index 99% rename from src/geeksforgeeks/ArticulationPoint.java rename to src/main/java/graph/ArticulationPoint.java index 20df9d4..8710fce 100644 --- a/src/geeksforgeeks/ArticulationPoint.java +++ b/src/main/java/graph/ArticulationPoint.java @@ -1,4 +1,4 @@ -package geeksforgeeks; +package graph; import java.util.ArrayList; import java.util.Collection; diff --git a/src/main/java/graph/MinimumJumpsToReachHome.java b/src/main/java/graph/MinimumJumpsToReachHome.java new file mode 100644 index 0000000..f204168 --- /dev/null +++ b/src/main/java/graph/MinimumJumpsToReachHome.java @@ -0,0 +1,57 @@ +package graph; + +import java.util.LinkedList; +import java.util.Queue; + +//https://leetcode.com/problems/minimum-jumps-to-reach-home/description/ +public class MinimumJumpsToReachHome { + static class Pair { + int pos; + boolean dir; + public Pair(int pos, boolean dir) { + this.pos = pos; + this.dir = dir; + } + } + public int minimumJumps(int[] forbidden, int a, int b, int x) { + int limit = 2000 + 2 * b + 1; // could hardcode a value grater than given in the limit of the problem + boolean[] visited = new boolean[limit]; + for (int num: forbidden) { + visited[num] = true; + } + int step = 0; + Queue q = new LinkedList<>(); + q.add(new Pair(0, false)); + visited[0] = true; + while (!q.isEmpty()) { + int size = q.size(); + for (int i = 0; i < size; i++) { + Pair p = q.poll(); + int pos = p.pos; + boolean dir = p.dir; + + if (pos == x) return step; + + if (!dir) { + int backward = pos - b; + + if (backward > 0 && !visited[backward]) { + q.offer(new Pair(backward, true)); + visited[backward] = true; + } + } + + int forward = pos + a; + + if (forward < limit && !visited[forward]) { + q.offer(new Pair(forward, false)); + visited[forward] = true; + } + + + } + step++; + } + return -1; + } +} diff --git a/src/main/java/graph/adjacencyList/AdjacencyList.java b/src/main/java/graph/adjacencyList/AdjacencyList.java new file mode 100644 index 0000000..5cbb663 --- /dev/null +++ b/src/main/java/graph/adjacencyList/AdjacencyList.java @@ -0,0 +1,47 @@ + +package graph.adjacencyList; + +import java.util.LinkedList; +import java.util.List; + +public class AdjacencyList { + + public static void main(String[] args) { + List adjListArray[] = new LinkedList[5]; + AdjacencyList list = new AdjacencyList(); + list.add(1, 3, adjListArray); + list.add(1, 2, adjListArray); + list.add(2, 3, adjListArray); + list.add(3, 4, adjListArray); + list.add(4, 2, adjListArray); + list.add(5, 4, adjListArray); + + list.printList(adjListArray); + } + + private void printList(List[] adjListArray) { + for (int i = 0; i < adjListArray.length; i++) { + List list = adjListArray[i]; + System.out.print((i + 1) + "-->"); + boolean firstTime = true; + for (Integer val : list) { + if (!firstTime) + System.out.print(" - "); + else + firstTime = false; + System.out.print(val); + } + System.out.println(); + } + } + + private void add(int src, int dest, List[] adjListArray) { + int val = src - 1; + List list = adjListArray[val]; + if (null == list) + list = new LinkedList(); + list.add(dest); + adjListArray[val] = list; + } + +} diff --git a/src/main/java/graph/adjacencyMatrix/AdjacencyMatrix.java b/src/main/java/graph/adjacencyMatrix/AdjacencyMatrix.java new file mode 100644 index 0000000..0589973 --- /dev/null +++ b/src/main/java/graph/adjacencyMatrix/AdjacencyMatrix.java @@ -0,0 +1,42 @@ +package graph.adjacencyMatrix; + +public class AdjacencyMatrix { + + public static void main(String[] args) { + AdjacencyMatrix am = new AdjacencyMatrix(); + + int[][] arr = new int[5][5]; + am.add(1, 2, arr); + am.add(1, 5, arr); + am.add(1, 4, arr); + am.add(3, 2, arr); + am.add(5, 2, arr); + am.add(3, 4, arr); + am.add(4, 5, arr); + + am.printMatrix(arr); + am.removeLink(3, 4, arr); + System.out.println("*******"); + am.printMatrix(arr); + + } + + private void removeLink(int i, int j, int[][] arr) { + arr[i - 1][j - 1] = 0; + } + + private void printMatrix(int[][] arr) { + for (int i = 0; i < arr.length; i++) { + for (int j = 0; j < arr.length; j++) { + System.out.print(arr[i][j] + " "); + } + System.out.println(); + } + + } + + private void add(int i, int j, int[][] arr) { + arr[i - 1][j - 1] = 1; + } + +} diff --git a/src/main/java/graph/bellmanFord/BellmanFord.java b/src/main/java/graph/bellmanFord/BellmanFord.java new file mode 100644 index 0000000..ffda9d0 --- /dev/null +++ b/src/main/java/graph/bellmanFord/BellmanFord.java @@ -0,0 +1,56 @@ +package graph.bellmanFord; + +import java.util.HashMap; +import java.util.Map; + +public class BellmanFord { + + private static int INFINITY = 10000000; + + public static void main(String[] args) { + Graph graph = new Graph(); + graph.addEdges(0, 1, 4); + graph.addEdges(0, 2, 5); + graph.addEdges(0, 3, 8); + graph.addEdges(1, 2, -3); + graph.addEdges(2, 4, 4); + graph.addEdges(3, 4, 2); + graph.addEdges(4, 3, 1); + + BellmanFord bf = new BellmanFord(); + Vertex startVertex = graph.vertexMap.values().iterator().next(); + bf.shortestPath(graph, startVertex); + } + + private void shortestPath(Graph graph, Vertex startVertex) { + Map, Integer> distance = new HashMap<>(); + Map, Vertex> parent = new HashMap<>(); + + for (Vertex vertex : graph.vertexMap.values()) { + distance.put(vertex, BellmanFord.INFINITY); + parent.put(vertex, null); + } + distance.put(startVertex, 0); + + for (int i = 0; i < graph.vertexMap.size() - 1; i++) { + for (Edge edge : graph.allEdges) { + int j = distance.get(edge.u) + edge.weight; + if (j < distance.get(edge.v)) { + distance.put(edge.v, j); + parent.put(edge.v, edge.u); + } + } + } + + for (Edge edge : graph.allEdges) { + int j = distance.get(edge.u) + edge.weight; + if (j < distance.get(edge.v)) { + throw new NegativeException(); + } + } + for (Map.Entry, Integer> map : distance.entrySet()) { + System.out.println(map.getKey().key + "-->" + map.getValue()); + } + } + +} diff --git a/src/main/java/graph/bellmanFord/Edge.java b/src/main/java/graph/bellmanFord/Edge.java new file mode 100644 index 0000000..513013b --- /dev/null +++ b/src/main/java/graph/bellmanFord/Edge.java @@ -0,0 +1,15 @@ +package graph.bellmanFord; + +public class Edge { + + Vertex u; + Vertex v; + int weight; + + public Edge(Vertex u, Vertex v, int weight) { + this.u = u; + this.v = v; + this.weight = weight; + } + +} diff --git a/src/main/java/graph/bellmanFord/Graph.java b/src/main/java/graph/bellmanFord/Graph.java new file mode 100644 index 0000000..517f46d --- /dev/null +++ b/src/main/java/graph/bellmanFord/Graph.java @@ -0,0 +1,35 @@ +package graph.bellmanFord; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class Graph { + + HashMap> vertexMap = new HashMap>(); + List> allEdges = new ArrayList>(); + + public void addEdges(T u, T v, int weight) { + Vertex vertex1 = null; + if (vertexMap.containsKey(u)) { + vertex1 = vertexMap.get(u); + } else { + vertex1 = new Vertex(u); + vertexMap.put(u, vertex1); + } + + Vertex vertex2 = null; + if (vertexMap.containsKey(v)) { + vertex2 = vertexMap.get(v); + } else { + vertex2 = new Vertex(v); + vertexMap.put(v, vertex2); + } + + Edge edge = new Edge(vertex1, vertex2, weight); + allEdges.add(edge); + + vertex1.addAdjacentVertex(vertex2, edge); + } + +} diff --git a/src/main/java/graph/bellmanFord/NegativeException.java b/src/main/java/graph/bellmanFord/NegativeException.java new file mode 100644 index 0000000..3010279 --- /dev/null +++ b/src/main/java/graph/bellmanFord/NegativeException.java @@ -0,0 +1,10 @@ +package graph.bellmanFord; + +public class NegativeException extends RuntimeException { + + /** + * + */ + private static final long serialVersionUID = 1L; + +} diff --git a/src/main/java/graph/bellmanFord/Vertex.java b/src/main/java/graph/bellmanFord/Vertex.java new file mode 100644 index 0000000..291b470 --- /dev/null +++ b/src/main/java/graph/bellmanFord/Vertex.java @@ -0,0 +1,20 @@ +package graph.bellmanFord; + +import java.util.ArrayList; +import java.util.List; + +class Vertex { + T key; + List> adjacentVertex = new ArrayList>(); + List> edges = new ArrayList>(); + + public Vertex(T key) { + this.key = key; + } + + public void addAdjacentVertex(Vertex vertex2, Edge edge) { + adjacentVertex.add(vertex2); + edges.add(edge); + } + +} \ No newline at end of file diff --git a/src/main/java/graph/breadthFirstSearch/BreadthFirstSearch.java b/src/main/java/graph/breadthFirstSearch/BreadthFirstSearch.java new file mode 100644 index 0000000..e07c57a --- /dev/null +++ b/src/main/java/graph/breadthFirstSearch/BreadthFirstSearch.java @@ -0,0 +1,53 @@ +package graph.breadthFirstSearch; + +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +public class BreadthFirstSearch { + + public static void main(String[] args) { + BreadthFirstSearch bfs = new BreadthFirstSearch(); + Graph graph = new Graph(5); + bfs.addEdges(1, 2, graph); + bfs.addEdges(2, 3, graph); + bfs.addEdges(3, 4, graph); + bfs.addEdges(4, 5, graph); + bfs.addEdges(2, 4, graph); + bfs.addEdges(1, 5, graph); + + bfs.print(1, graph); + + } + + private void print(int i, Graph graph) { + Queue queue = new LinkedList(); + queue.add(i); + Queue clone = new LinkedList(queue); + exploreVertex(queue, clone, graph); + + for (Integer val : clone) { + System.out.print(val + 1 + "->"); + } + } + + private void exploreVertex(Queue queue, Queue clone, Graph graph) { + while (!queue.isEmpty()) { + Integer poll = queue.poll(); + List list = graph.adjacentList[poll]; + for (Integer value : list) { + if (!clone.contains(value)) { + clone.add(value); + queue.add(value); + } + } + } + } + + private void addEdges(int src, int dest, Graph graph) { + int srcVal = src - 1; + int destVal = dest - 1; + graph.adjacentList[srcVal].add(destVal); + graph.adjacentList[destVal].add(srcVal); + } +} diff --git a/src/main/java/graph/breadthFirstSearch/Graph.java b/src/main/java/graph/breadthFirstSearch/Graph.java new file mode 100644 index 0000000..4b5b3c5 --- /dev/null +++ b/src/main/java/graph/breadthFirstSearch/Graph.java @@ -0,0 +1,18 @@ +package graph.breadthFirstSearch; + +import java.util.LinkedList; +import java.util.List; + +public class Graph { + + List adjacentList[]; + int vertices; + + public Graph(int v) { + vertices = v; + adjacentList = new LinkedList[vertices]; + for (int i = 0; i < adjacentList.length; i++) { + adjacentList[i] = new LinkedList<>(); + } + } +} diff --git a/src/main/java/graph/cycle/CycleInDirectedGraph.java b/src/main/java/graph/cycle/CycleInDirectedGraph.java new file mode 100644 index 0000000..2e823e9 --- /dev/null +++ b/src/main/java/graph/cycle/CycleInDirectedGraph.java @@ -0,0 +1,67 @@ +package graph.cycle; + +import java.util.HashSet; +import java.util.Set; + +/** + * http://www.geeksforgeeks.org/detect-cycle-in-a-graph/ + */ +public class CycleInDirectedGraph { + + public boolean hasCycle(Graph graph) { + Set> whiteSet = new HashSet<>(); + Set> graySet = new HashSet<>(); + Set> blackSet = new HashSet<>(); + + graph.getAllVertex().forEach(vertex -> whiteSet.add(vertex)); + + while (whiteSet.size() > 0) { + Vertex current = whiteSet.iterator().next(); + if (dfs(current, whiteSet, graySet, blackSet)) { + return true; + } + } + return false; + } + + private boolean dfs(Vertex current, Set> whiteSet, Set> graySet, + Set> blackSet) { + // move current to gray set from white set and then explore it. + moveVertex(current, whiteSet, graySet); + for (Vertex neighbor : current.getAdjacentVertexes()) { + // if in black set means already explored so continue. + if (blackSet.contains(neighbor)) { + continue; + } + // if in gray set then cycle found. + if (graySet.contains(neighbor)) { + return true; + } + if (dfs(neighbor, whiteSet, graySet, blackSet)) { + return true; + } + } + // move vertex from gray set to black set when done exploring. + moveVertex(current, graySet, blackSet); + return false; + } + + private void moveVertex(Vertex vertex, Set> sourceSet, + Set> destinationSet) { + sourceSet.remove(vertex); + destinationSet.add(vertex); + } + + public static void main(String args[]) { + Graph graph = new Graph<>(true); + graph.addEdge(1, 2); + graph.addEdge(1, 3); + graph.addEdge(2, 3); + graph.addEdge(4, 1); + graph.addEdge(4, 5); + graph.addEdge(5, 6); + graph.addEdge(6, 4); + CycleInDirectedGraph cdg = new CycleInDirectedGraph(); + System.out.println(cdg.hasCycle(graph)); + } +} \ No newline at end of file diff --git a/src/main/java/graph/cycle/CycleUndirectedGraph.java b/src/main/java/graph/cycle/CycleUndirectedGraph.java new file mode 100644 index 0000000..66c7955 --- /dev/null +++ b/src/main/java/graph/cycle/CycleUndirectedGraph.java @@ -0,0 +1,91 @@ +package graph.cycle; + + +import graph.interview.DisjointSet; + +import java.util.HashSet; +import java.util.Set; + +/** + * Date 10/11/2014 + * + * @author Tushar Roy + * + * Given an undirected graph find cycle in this graph. + * + * Solution This can be solved in many ways. Below is the code to solve + * it using disjoint sets and DFS. + * + * Runtime and space complexity for both the techniques is O(v) where v + * is total number of vertices in the graph. + */ +public class CycleUndirectedGraph { + + public boolean hasCycleUsingDisjointSets(Graph graph) { + DisjointSet disjointSet = new DisjointSet(); + + for (Vertex vertex : graph.getAllVertex()) { + disjointSet.makeSet(vertex.getId()); + } + + for (Edge edge : graph.getAllEdges()) { + long parent1 = disjointSet.findParent(edge.getVertex1().getId()); + long parent2 = disjointSet.findParent(edge.getVertex2().getId()); + if (parent1 == parent2) { + return true; + } + disjointSet.union(edge.getVertex1().getId(), edge.getVertex2().getId()); + } + return false; + } + + public boolean hasCycleDFS(Graph graph) { + Set> visited = new HashSet>(); + for (Vertex vertex : graph.getAllVertex()) { + if (visited.contains(vertex)) { + continue; + } + boolean flag = hasCycleDFSUtil(vertex, visited, null); + if (flag) { + return true; + } + } + return false; + } + + public boolean hasCycleDFSUtil(Vertex vertex, Set> visited, Vertex parent) { + visited.add(vertex); + for (Vertex adj : vertex.getAdjacentVertexes()) { + if (!adj.equals(parent)) { + + if (visited.contains(adj)) { + return true; + } + boolean hasCycle = hasCycleDFSUtil(adj, visited, vertex); + if (hasCycle) { + return true; + } + } + } + return false; + } + + public static void main(String args[]) { + + CycleUndirectedGraph cycle = new CycleUndirectedGraph(); + Graph graph = new Graph(false); + + graph.addEdge(0, 1); + graph.addEdge(1, 2); + graph.addEdge(0, 3); + graph.addEdge(3, 4); + graph.addEdge(4, 5); + graph.addEdge(5, 1); + boolean isCycle = cycle.hasCycleDFS(graph); + System.out.println(isCycle); + isCycle = cycle.hasCycleUsingDisjointSets(graph); + System.out.print(isCycle); + + } + +} \ No newline at end of file diff --git a/src/main/java/graph/cycle/Graph.java b/src/main/java/graph/cycle/Graph.java new file mode 100644 index 0000000..28ad0cd --- /dev/null +++ b/src/main/java/graph/cycle/Graph.java @@ -0,0 +1,240 @@ +package graph.cycle; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class Graph { + + private List> allEdges; + private Map> allVertex; + boolean isDirected = false; + + public Graph(boolean isDirected) { + allEdges = new ArrayList>(); + allVertex = new HashMap>(); + this.isDirected = isDirected; + } + + public void addEdge(long id1, long id2) { + addEdge(id1, id2, 0); + } + + public void addVertex(Vertex vertex) { + if (allVertex.containsKey(vertex.getId())) { + return; + } + allVertex.put(vertex.getId(), vertex); + for (Edge edge : vertex.getEdges()) { + allEdges.add(edge); + } + } + + public Vertex addSingleVertex(long id) { + if (allVertex.containsKey(id)) { + return allVertex.get(id); + } + Vertex v = new Vertex(id); + allVertex.put(id, v); + return v; + } + + public Vertex getVertex(long id) { + return allVertex.get(id); + } + + public void addEdge(long id1, long id2, int weight) { + Vertex vertex1 = null; + if (allVertex.containsKey(id1)) { + vertex1 = allVertex.get(id1); + } else { + vertex1 = new Vertex(id1); + allVertex.put(id1, vertex1); + } + Vertex vertex2 = null; + if (allVertex.containsKey(id2)) { + vertex2 = allVertex.get(id2); + } else { + vertex2 = new Vertex(id2); + allVertex.put(id2, vertex2); + } + + Edge edge = new Edge(vertex1, vertex2, isDirected, weight); + allEdges.add(edge); + vertex1.addAdjacentVertex(edge, vertex2); + if (!isDirected) { + vertex2.addAdjacentVertex(edge, vertex1); + } + } + + public List> getAllEdges() { + return allEdges; + } + + public Collection> getAllVertex() { + return allVertex.values(); + } + + public void setDataForVertex(long id, T data) { + if (allVertex.containsKey(id)) { + Vertex vertex = allVertex.get(id); + vertex.setData(data); + } + } + + @Override + public String toString() { + StringBuffer buffer = new StringBuffer(); + for (Edge edge : getAllEdges()) { + buffer.append(edge.getVertex1() + " " + edge.getVertex2() + " " + edge.getWeight()); + buffer.append("\n"); + } + return buffer.toString(); + } +} + +class Vertex { + long id; + private T data; + private List> edges = new ArrayList<>(); + private List> adjacentVertex = new ArrayList<>(); + + Vertex(long id) { + this.id = id; + } + + public long getId() { + return id; + } + + public void setData(T data) { + this.data = data; + } + + public T getData() { + return data; + } + + public void addAdjacentVertex(Edge e, Vertex v) { + edges.add(e); + adjacentVertex.add(v); + } + + public String toString() { + return String.valueOf(id); + } + + public List> getAdjacentVertexes() { + return adjacentVertex; + } + + public List> getEdges() { + return edges; + } + + public int getDegree() { + return edges.size(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (int) (id ^ (id >>> 32)); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Vertex other = (Vertex) obj; + if (id != other.id) + return false; + return true; + } +} + +class Edge { + private boolean isDirected = false; + private Vertex vertex1; + private Vertex vertex2; + private int weight; + + Edge(Vertex vertex1, Vertex vertex2) { + this.vertex1 = vertex1; + this.vertex2 = vertex2; + } + + Edge(Vertex vertex1, Vertex vertex2, boolean isDirected, int weight) { + this.vertex1 = vertex1; + this.vertex2 = vertex2; + this.weight = weight; + this.isDirected = isDirected; + } + + Edge(Vertex vertex1, Vertex vertex2, boolean isDirected) { + this.vertex1 = vertex1; + this.vertex2 = vertex2; + this.isDirected = isDirected; + } + + Vertex getVertex1() { + return vertex1; + } + + Vertex getVertex2() { + return vertex2; + } + + int getWeight() { + return weight; + } + + public boolean isDirected() { + return isDirected; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((vertex1 == null) ? 0 : vertex1.hashCode()); + result = prime * result + ((vertex2 == null) ? 0 : vertex2.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Edge other = (Edge) obj; + if (vertex1 == null) { + if (other.vertex1 != null) + return false; + } else if (!vertex1.equals(other.vertex1)) + return false; + if (vertex2 == null) { + if (other.vertex2 != null) + return false; + } else if (!vertex2.equals(other.vertex2)) + return false; + return true; + } + + @Override + public String toString() { + return "Edge [isDirected=" + isDirected + ", vertex1=" + vertex1 + ", vertex2=" + vertex2 + ", weight=" + weight + + "]"; + } +} diff --git a/src/main/java/graph/depthFirstSearch/DepthFirstSearch.java b/src/main/java/graph/depthFirstSearch/DepthFirstSearch.java new file mode 100644 index 0000000..73b59a4 --- /dev/null +++ b/src/main/java/graph/depthFirstSearch/DepthFirstSearch.java @@ -0,0 +1,55 @@ +package graph.depthFirstSearch; + +import java.util.LinkedList; +import java.util.Stack; + +public class DepthFirstSearch { + + public static void main(String[] args) { + DepthFirstSearch dfs = new DepthFirstSearch(); + Graph graph = new Graph(); + + dfs.addEdges('A', 'B', graph); + dfs.addEdges('C', 'B', graph); + dfs.addEdges('C', 'D', graph); + dfs.addEdges('D', 'E', graph); + dfs.addEdges('A', 'E', graph); + dfs.addEdges('D', 'B', graph); + + dfs.print(graph, 'B'); + } + + private void addEdges(char src, char dest, Graph graph) { + checkAndAdd(src, dest, graph); + checkAndAdd(dest, src, graph); + } + + private void checkAndAdd(char src, char dest, Graph graph) { + graph.adjacentMap.getOrDefault(src,new LinkedList<>()).add(dest); + + } + + private void print(Graph graph, Character src) { + Stack stack = new Stack(); + stack.push(src); + LinkedList list = new LinkedList(); + list.add(src); + + while (!stack.isEmpty()) { + Character pop = stack.pop(); + LinkedList linkedList = graph.adjacentMap.get(pop); + if (!linkedList.isEmpty()) { + Character removeFirst = linkedList.removeFirst(); + stack.push(removeFirst); + if (!list.contains(removeFirst)) + list.add(removeFirst); + } + } + + for (Character ch : list) { + System.out.println(ch); + } + + } + +} diff --git a/src/main/java/graph/depthFirstSearch/Graph.java b/src/main/java/graph/depthFirstSearch/Graph.java new file mode 100644 index 0000000..9ba4180 --- /dev/null +++ b/src/main/java/graph/depthFirstSearch/Graph.java @@ -0,0 +1,14 @@ +package graph.depthFirstSearch; + +import java.util.HashMap; +import java.util.LinkedList; + +public class Graph { + + HashMap> adjacentMap; + + public Graph() { + adjacentMap = new HashMap>(); + } + +} diff --git a/src/main/java/graph/dijkstraAlgorithm/BinaryHeap.java b/src/main/java/graph/dijkstraAlgorithm/BinaryHeap.java new file mode 100644 index 0000000..07b7e80 --- /dev/null +++ b/src/main/java/graph/dijkstraAlgorithm/BinaryHeap.java @@ -0,0 +1,125 @@ +package graph.dijkstraAlgorithm; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class BinaryHeap { + + List allNodes = new ArrayList(); + Map positionMap = new HashMap(); + + class Node { + long key; + int weight; + + public Node(long key, int weight) { + this.key = key; + this.weight = weight; + } + + @Override + public String toString() { + return this.key + " " + this.weight; + } + + } + + protected Node extractMin() { + Node node = allNodes.get(0); + int size = allNodes.size() - 1; + allNodes.set(0, allNodes.get(size)); + positionMap.remove(node.key); + positionMap.put(allNodes.get(size).key, 0); + allNodes.remove(size); + + int currentPosition = 0; + int left = 1; + int right = 2; + + if (size > 1) { + Node currentNode = allNodes.get(0); + Node leftNode = null; + Node rightNode = null; + + if (left < size) { + leftNode = allNodes.get(left); + } + if (right < size) { + rightNode = allNodes.get(right); + } + int smallValuePosition = ((rightNode != null) && (rightNode.weight < leftNode.weight)) ? right : left; + + Node parentNode = allNodes.get(smallValuePosition); + swapNodes(currentPosition, currentNode, smallValuePosition, parentNode); + updatePositionMap(currentPosition, smallValuePosition, currentNode.key, parentNode.key); + + left = 2 * smallValuePosition + 1; + right = 2 * smallValuePosition + 2; + currentNode = allNodes.get(smallValuePosition); + } + return node; + } + + protected void addNode(int i, long key) { + Node node = new Node(key, i); + allNodes.add(node); + int currentPosition = allNodes.size() - 1; + int parentPosition = (currentPosition - 1) / 2; + + positionMap.put(key, currentPosition); + + Node parentNode = allNodes.get(parentPosition); + Node currentNode = allNodes.get(currentPosition); + while (parentNode.weight > currentNode.weight) { + swapNodes(currentPosition, currentNode, parentPosition, parentNode); + updatePositionMap(currentPosition, parentPosition, currentNode.key, parentNode.key); + + currentPosition = parentPosition; + parentPosition = (currentPosition - 1) / 2; + + currentNode = allNodes.get(currentPosition); + parentNode = allNodes.get(parentPosition); + } + } + + private void updatePositionMap(int currentPosition, int parentPosition, long current, long parent) { + positionMap.put(current, parentPosition); + positionMap.put(parent, currentPosition); + } + + private void swapNodes(int currentPosition, Node currentNode, int parentPosition, + Node parentNode) { + allNodes.set(currentPosition, parentNode); + allNodes.set(parentPosition, currentNode); + } + + public void decrease(long vertex, int i) { + + Integer position = positionMap.get(vertex); + allNodes.get(position).weight = i; + + int parent = (position - 1) / 2; + + while (parent >= 0) { + if (allNodes.get(parent).weight > allNodes.get(position).weight) { + swapNodes(position, allNodes.get(position), parent, allNodes.get(parent)); + updatePositionMap(position, parent, allNodes.get(parent).key, allNodes.get(position).key); + position = parent; + parent = (parent - 1) / 2; + } else { + break; + } + } + + } + + public boolean isEmpty() { + return allNodes.isEmpty(); + } + + public boolean containsData(Vertex adjacentVertex) { + return positionMap.containsKey(adjacentVertex.key); + } +} diff --git a/src/main/java/graph/dijkstraAlgorithm/DijkstraAlgorithm.java b/src/main/java/graph/dijkstraAlgorithm/DijkstraAlgorithm.java new file mode 100644 index 0000000..3371318 --- /dev/null +++ b/src/main/java/graph/dijkstraAlgorithm/DijkstraAlgorithm.java @@ -0,0 +1,64 @@ +package graph.dijkstraAlgorithm; + +import java.util.HashMap; +import java.util.List; + +public class DijkstraAlgorithm { + + public static void main(String[] args) { + Graph graph = new Graph(); + graph.addEdge(1, 2, 5); + graph.addEdge(1, 5, 2); + graph.addEdge(1, 4, 9); + graph.addEdge(2, 3, 2); + graph.addEdge(3, 4, 3); + graph.addEdge(4, 6, 2); + graph.addEdge(5, 6, 3); + + DijkstraAlgorithm dijkstra = new DijkstraAlgorithm(); + dijkstra.singleSourceShortestPath(graph); + } + + private void singleSourceShortestPath(Graph graph) { + BinaryHeap heap = new BinaryHeap(); + HashMap distanceMap = new HashMap(); + HashMap parentMap = new HashMap(); + + Vertex vertex = graph.vertexMap.get((long) 1); + + for (Vertex ver : graph.vertexMap.values()) { + heap.addNode(Integer.MAX_VALUE, ver.key); + } + heap.decrease(vertex.key, 0); + distanceMap.put(vertex, 0); + while (!heap.isEmpty()) { + long currentKey = heap.extractMin().key; + Vertex currentVertex = graph.vertexMap.get(currentKey); + + System.out.println(currentVertex.key); + List allEdges = currentVertex.allEdges; + + for (Edge edge : allEdges) { + Vertex adjacentVertex = getAdjacentVertex(currentVertex, edge); + if (heap.containsData(adjacentVertex)) { + int weight = 0; + if (distanceMap.containsKey(adjacentVertex)) { + weight = distanceMap.get(adjacentVertex); + } + weight = edge.weight + weight; + if (heap.allNodes.get(heap.positionMap.get(adjacentVertex.key)).weight > weight) { + heap.decrease(adjacentVertex.key, weight); + parentMap.put(adjacentVertex, currentVertex); + distanceMap.put(adjacentVertex, weight); + } + } + } + } + } + + private Vertex getAdjacentVertex(Vertex currentVertex, Edge edge) { + return edge.vertex1.key == currentVertex.key ? edge.vertex2 : edge.vertex1; + + } + +} diff --git a/src/main/java/graph/dijkstraAlgorithm/Graph.java b/src/main/java/graph/dijkstraAlgorithm/Graph.java new file mode 100644 index 0000000..2e32125 --- /dev/null +++ b/src/main/java/graph/dijkstraAlgorithm/Graph.java @@ -0,0 +1,72 @@ +package graph.dijkstraAlgorithm; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class Graph { + + Map vertexMap = new HashMap(); + List allEdges = new ArrayList(); + + public void addEdge(long v1, long v2, int weight) { + Vertex vertex1 = null; + if (vertexMap.containsKey(v1)) { + vertex1 = vertexMap.get(v1); + } else { + vertex1 = new Vertex(v1); + vertexMap.put(v1, vertex1); + } + + Vertex vertex2 = null; + if (vertexMap.containsKey(v2)) { + vertex2 = vertexMap.get(v2); + } else { + vertex2 = new Vertex(v2); + vertexMap.put(v2, vertex2); + } + + Edge edge = new Edge(vertex1, vertex2, weight); + allEdges.add(edge); + vertex1.adjacentVertexAndEdge(vertex2, edge); + vertex2.adjacentVertexAndEdge(vertex1, edge); + } +} + +class Vertex { + long key; + List adjacentVertex = new ArrayList(); + List allEdges = new ArrayList(); + + public Vertex(long key) { + this.key = key; + } + + public void adjacentVertexAndEdge(Vertex vertex, Edge edge) { + allEdges.add(edge); + adjacentVertex.add(vertex); + } + + @Override + public String toString() { + return this.key + ""; + } +} + +class Edge { + Vertex vertex1; + Vertex vertex2; + int weight; + + public Edge(Vertex vertex1, Vertex vertex2, int weight) { + this.vertex1 = vertex1; + this.vertex2 = vertex2; + this.weight = weight; + } + + @Override + public String toString() { + return this.vertex1 + "-->" + this.vertex2; + } +} \ No newline at end of file diff --git a/src/main/java/graph/disjoints/DisjointSetArrayImplementation.java b/src/main/java/graph/disjoints/DisjointSetArrayImplementation.java new file mode 100644 index 0000000..a9cf451 --- /dev/null +++ b/src/main/java/graph/disjoints/DisjointSetArrayImplementation.java @@ -0,0 +1,42 @@ +package graph.disjoints; + +public class DisjointSetArrayImplementation { + + static int[] arr = { -1, -1, -1, -1, -1, -1, -1, -1 }; + + public static void main(String[] args) { + DisjointSetArrayImplementation dsai = new DisjointSetArrayImplementation(); + + dsai.unionSet(0, 1); + dsai.unionSet(2, 3); + dsai.unionSet(4, 5); + dsai.unionSet(6, 7); + dsai.unionSet(1, 3); + dsai.unionSet(5, 6); + dsai.unionSet(1, 4); + dsai.unionSet(0, 2); + + for (int i = 0; i < arr.length; i++) { + System.out.print(arr[i] + " "); + } + } + + + private void unionSet(int src, int dest) { + src = findParent(src); + dest = findParent(dest); + if (src >= 0 && src == dest) { + System.out.println("found cycle"); + } + arr[src] = arr[src] - Math.abs(arr[dest]); + arr[dest] = src; + } + + private int findParent(int src) { + + if (arr[src] < 0) { + return src; + } + return findParent(arr[src]); + } +} diff --git a/src/main/java/graph/disjoints/DisjointSetWithNode.java b/src/main/java/graph/disjoints/DisjointSetWithNode.java new file mode 100644 index 0000000..0908c44 --- /dev/null +++ b/src/main/java/graph/disjoints/DisjointSetWithNode.java @@ -0,0 +1,61 @@ +package graph.disjoints; + +import java.util.HashMap; +import java.util.Map; + +public class DisjointSetWithNode { + + Map map = new HashMap(); + + public class Node { + int rank; + Node parent; + int data; + } + + private void makeSet(int data) { + Node node = new Node(); + node.data = data; + node.parent = node; + map.put(data, node); + } + + private void unionSet(int src, int dest) { + Node srcNode = findSet(src); + Node destNode = findSet(dest); + + if (srcNode.rank >= destNode.rank) { + srcNode.rank = srcNode.rank + (destNode.rank == 0 ? 1 : destNode.rank); + destNode.parent = srcNode; + } else { + srcNode.parent = destNode; + } + } + + private Node findSet(int src) { + Node node = map.get(src); + + if (node == node.parent) + return node; + + return findSet(node.parent.data); + } + + public static void main(String[] args) { + DisjointSetWithNode ds = new DisjointSetWithNode(); + ds.makeSet(1); + ds.makeSet(2); + ds.makeSet(3); + ds.makeSet(4); + ds.makeSet(5); + ds.makeSet(6); + ds.makeSet(7); + + ds.unionSet(1, 2); + ds.unionSet(3, 4); + ds.unionSet(2, 3); + + System.out.println(ds.findSet(4).data); + + } +} diff --git a/src/main/java/graph/disjoints/PredatorDisjointSet.java b/src/main/java/graph/disjoints/PredatorDisjointSet.java new file mode 100644 index 0000000..44a03d7 --- /dev/null +++ b/src/main/java/graph/disjoints/PredatorDisjointSet.java @@ -0,0 +1,53 @@ +package graph.disjoints; + +import java.util.ArrayList; +import java.util.List; + +public class PredatorDisjointSet { + + public static int minimumGroups(List predators) { + int[] arr = new int[predators.size()]; + int index = 0; + int result = 0; + for (Integer val : predators) { + if (val == -1) { + arr[index] = 0; + } else { + result = makeSet(val, arr, result); + arr[index] = result; + } + index++; + } + return 0; + } + + public static int makeSet(int val, int[] arr, int result) { + int parent = findParent(val, arr); + if (parent == 0) { + return result + 1; + } + return result; + } + + public static int findParent(int val, int[] arr) { + if (val == 0) { + return 0; + } + return findParent(arr[val], arr); + } + + public static void main(String[] args) { + List list = new ArrayList(); + list.add(-1); + list.add(8); + list.add(6); + list.add(0); + list.add(7); + list.add(3); + list.add(8); + list.add(9); + list.add(-1); + list.add(6); + minimumGroups(list); + } +} diff --git a/src/main/java/graph/disjoints/RankTransformMatrix.java b/src/main/java/graph/disjoints/RankTransformMatrix.java new file mode 100644 index 0000000..4643c0f --- /dev/null +++ b/src/main/java/graph/disjoints/RankTransformMatrix.java @@ -0,0 +1,147 @@ +package graph.disjoints; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class RankTransformMatrix { + static class Position { + int index; // the index is just the index in a list of positions, it is needed for union-find + int row; + int column; + + Position(int index, int row, int column) { + this.index = index; + this.row = row; + this.column = column; + } + } + + public int[][] matrixRankTransform(int[][] matrix) { + + int[][] ans = new int[matrix.length][matrix[0].length]; + int[] maxRankInCol = new int[matrix[0].length]; + int[] maxRankInRow = new int[matrix.length]; + Map> map = new HashMap<>(); + List uniqueNumbers = new ArrayList<>(); + + // populate map and the list of unique numbers + for (int i = 0; i < matrix.length; i++) + for (int j = 0; j < matrix[0].length; j++) { + if (!map.containsKey(matrix[i][j])) uniqueNumbers.add(matrix[i][j]); + map.putIfAbsent(matrix[i][j], new ArrayList<>()); + map.get(matrix[i][j]).add(new Position(map.get(matrix[i][j]).size(), i, j)); + } + + // sort the unique numbers to iterate from the smallest to the largest + + Collections.sort(uniqueNumbers); + + // For every unique number, we split the positions with this number into groups of non-adjacent ones using union-find. + // Then for each group set the rank of the elements in the group to be (the largest rank among all the rows and columns + // that contain elements of that group) + 1. + // Then update max ranks of those rows and columns to be the new rank. + + for (int n : uniqueNumbers) { + List positions = map.get(n); + UnionFind uf = new UnionFind(positions.size()); + + // union all the positions that have the same row or column + // for this I sorted by row/column and took union of adjacent positions + + positions.sort(Comparator.comparingInt(p -> p.row)); + for (int i = 0; i < positions.size() - 1; i++) + if (positions.get(i).row == positions.get(i + 1).row) + uf.union(positions.get(i).index, positions.get(i + 1).index); + + positions.sort(Comparator.comparingInt(p -> p.column)); + for (int i = 0; i < positions.size() - 1; i++) + if (positions.get(i).column == positions.get(i + 1).column) + uf.union(positions.get(i).index, positions.get(i + 1).index); + + // for every group (positions with the same parent), make a separate entry in a map + + HashMap> posMap = new HashMap<>(); + for (Position position : positions) { + int parent = uf.find(position.index); + if (!posMap.containsKey(parent)) posMap.put(parent, new ArrayList<>()); + posMap.get(parent).add(position); + } + + // for every list in our map (which are the groups), find the rank and update the ranks of rows/columns + + for (List list : posMap.values()) { + + // find max rank among rows/columns that contain elements of this group + + int max = 0; + for (Position pos : list) { + max = Math.max(maxRankInRow[pos.row], max); + max = Math.max(maxRankInCol[pos.column], max); + } + + // update maximums to the new rank + + int rank = max + 1; + for (Position pos : list) { + ans[pos.row][pos.column] = rank; + maxRankInRow[pos.row] = rank; + maxRankInCol[pos.column] = rank; + } + } + } + + return ans; + } + + /*** + /* The rest is Union-Find class (rank + path compression) + ***/ + + private static class UnionFind { + private int[] parent; + private int[] rank; + + UnionFind(int n) { + parent = new int[n]; + rank = new int[n]; + for (int i = 0; i < n; i++) { + parent[i] = i; + rank[i] = 0; + } + } + + int find(int p) { + if (p == parent[p]) return p; + parent[p] = find(parent[p]); + return parent[p]; + } + + void union(int p, int q) { + int rootP = find(p), rootQ = find(q); + if (rootP == rootQ) return; + if (rank[rootP] < rank[rootQ]) + parent[rootP] = rootQ; + else if (rank[rootP] > rank[rootQ]) + parent[rootQ] = rootP; + else { + parent[rootQ] = rootP; + rank[rootP]++; + } + } + } + + public static void main(String[] args) { + new RankTransformMatrix().matrixRankTransform(new int[][]{ {20, -21, 14}, + {-19, 4, 19}, + {22, -47, 24}, + {-19, 4, 19}}); + //new int[][]{{7,3,6},{1,4,5},{9,8,2}}; + } + +} + + diff --git a/src/main/java/graph/floydwarshall/FloydWarshall.java b/src/main/java/graph/floydwarshall/FloydWarshall.java new file mode 100644 index 0000000..6fe107e --- /dev/null +++ b/src/main/java/graph/floydwarshall/FloydWarshall.java @@ -0,0 +1,29 @@ + package graph.floydwarshall; + +public class FloydWarshall { + + private static int INF = 100000; + + public static void main(String[] args) { + int[][] graph = { { 0, 3, INF, 7 }, { 8, 0, 2, INF }, { 5, INF, 0, 1 }, { 2, INF, INF, 0 } }; + FloydWarshall floyd = new FloydWarshall(); + floyd.shortestPath(graph); + } + + private void shortestPath(int[][] graph) { + for (int k = 0; k < graph.length; k++) { + for (int i = 0; i < graph.length; i++) { + for (int j = 0; j < graph[i].length; j++) { + if (i != k && j != k && i != j) { + if (graph[i][j] > graph[i][k] + graph[k][j]) { + graph[i][j] = graph[i][k] + graph[k][j]; + } + } + System.out.print(graph[i][j] + " "); + } + System.out.println(); + } + System.out.println(); + } + } +} diff --git a/src/main/java/graph/interview/DisjointSet.java b/src/main/java/graph/interview/DisjointSet.java new file mode 100644 index 0000000..fcfa221 --- /dev/null +++ b/src/main/java/graph/interview/DisjointSet.java @@ -0,0 +1,178 @@ +package graph.interview; + +import java.util.HashMap; +import java.util.Map; + +public class DisjointSet { + + private Map map = new HashMap<>(); + + class Node { + long data; + Node parent; + int rank; + } + + /** + * Create a set with only one element. + */ + public void makeSet(long data) { + Node node = new Node(); + node.data = data; + node.parent = node; + node.rank = 0; + map.put(data, node); + } + + /** + * Combines two sets together to one. Does union by rank + * + * @return true if data1 and data2 are in different set before union else false. + */ + public boolean union(long data1, long data2) { + Node node1 = map.get(data1); + Node node2 = map.get(data2); + + Node parent1 = findParent(node1); + Node parent2 = findParent(node2); + + // if they are part of same set do nothing + if (parent1.data == parent2.data) { + return false; + } + + // else whoever's rank is higher becomes parent of other + if (parent1.rank >= parent2.rank) { + // increment rank only if both sets have same rank + parent1.rank = (parent1.rank == parent2.rank) ? parent1.rank + 1 : parent1.rank; + parent2.parent = parent1; + } else { + parent1.parent = parent2; + } + return true; + } + + /** + * Finds the representative of this set + */ + public long findParent(long data) { + return findParent(map.get(data)).data; + } + + /** + * Find the representative recursively and does path compression as well. + */ + private Node findParent(Node node) { + Node parent = node.parent; + if (parent == node) { + return parent; + } + node.parent = findParent(node.parent); + return node.parent; + } + + public static void main(String args[]) { + DisjointSet ds = new DisjointSet(); + ds.makeSet(1); + ds.makeSet(2); + ds.makeSet(3); + ds.makeSet(4); + ds.makeSet(5); + ds.makeSet(6); + ds.makeSet(7); + + ds.union(1, 2); + ds.union(2, 3); + ds.union(4, 5); + ds.union(6, 7); + ds.union(5, 6); + ds.union(3, 7); + + System.out.println(ds.findParent(1)); + System.out.println(ds.findParent(2)); + System.out.println(ds.findParent(3)); + System.out.println(ds.findParent(4)); + System.out.println(ds.findParent(5)); + System.out.println(ds.findParent(6)); + System.out.println(ds.findParent(7)); + } + + +} + + class DisjointSetLeetcode { + + private int[] parent; + private byte[] rank; + + public DisjointSetLeetcode(int n) { + if (n < 0) throw new IllegalArgumentException(); + parent = new int[n]; + rank = new byte[n]; + } + + public int find(int x) { + if (parent[x] == 0) return x; + return parent[x] = find(parent[x]); // Path compression by halving. + } + + // Return false if x, y are connected. + public boolean union(int x, int y) { + int rootX = find(x); + int rootY = find(y); + if (rootX == rootY) return false; + + // Make root of smaller rank point to root of larger rank. + if (rank[rootX] < rank[rootY]) parent[rootX] = rootY; + else if (rank[rootX] > rank[rootY]) parent[rootY] = rootX; + else { + parent[rootX] = rootY; + rank[rootY]++; + } + + return true; + } + } + +class UnionFind { + int[] sets; + int[] size; + int count; + + public UnionFind(int n) { + sets = new int[n]; + size = new int[n]; + count = n; + + for (int i = 0; i < n; i++) { + sets[i] = i; + size[i] = 1; + } + } + + public int find(int node) { + while (node != sets[node]) { + node = sets[node]; + } + return node; + } + + public void union(int i, int j) { + int node1 = find(i); + int node2 = find(j); + + if (node1 == node2) { + return; + } + + if (size[node1] < size[node2]) { + sets[node1] = node2; + size[node2] += size[node1]; + } + else { + sets[node2] = node1; + size[node1] += size[node2]; + } + --count; + } +} \ No newline at end of file diff --git a/src/main/java/graph/kruskalAlgorithm/DisjointSet.java b/src/main/java/graph/kruskalAlgorithm/DisjointSet.java new file mode 100644 index 0000000..46aca4e --- /dev/null +++ b/src/main/java/graph/kruskalAlgorithm/DisjointSet.java @@ -0,0 +1,81 @@ +package graph.kruskalAlgorithm; + +import java.util.HashMap; +import java.util.Map; + +public class DisjointSet { + + private Map map = new HashMap<>(); + + class Node { + long data; + Node parent; + int rank; + } + + public void makeSet(long data) { + Node node = new Node(); + node.data = data; + node.parent = node; + node.rank = 0; + map.put(data, node); + } + + public boolean union(long data1, long data2) { + Node node1 = map.get(data1); + Node node2 = map.get(data2); + + Node parent1 = findSet(node1); + Node parent2 = findSet(node2); + + if (parent1.data == parent2.data) { + return false; + } + + if (parent1.rank >= parent2.rank) { + parent1.rank = (parent1.rank == parent2.rank) ? parent1.rank + 1 : parent1.rank; + parent2.parent = parent1; + } else { + parent1.parent = parent2; + } + return true; + } + + public long findSet(long data) { + return findSet(map.get(data)).data; + } + + private Node findSet(Node node) { + Node parent = node.parent; + if (parent == node) { + return parent; + } + return findSet(node.parent); + } + + public static void main(String args[]) { + DisjointSet ds = new DisjointSet(); + ds.makeSet(1); + ds.makeSet(2); + ds.makeSet(3); + ds.makeSet(4); + ds.makeSet(5); + ds.makeSet(6); + ds.makeSet(7); + + ds.union(1, 2); + ds.union(2, 3); + ds.union(4, 5); + ds.union(6, 7); + ds.union(5, 6); + ds.union(3, 7); + + System.out.println(ds.findSet(1)); + System.out.println(ds.findSet(2)); + System.out.println(ds.findSet(3)); + System.out.println(ds.findSet(4)); + System.out.println(ds.findSet(5)); + System.out.println(ds.findSet(6)); + System.out.println(ds.findSet(7)); + } +} \ No newline at end of file diff --git a/src/main/java/graph/kruskalAlgorithm/Graph.java b/src/main/java/graph/kruskalAlgorithm/Graph.java new file mode 100644 index 0000000..8acdf43 --- /dev/null +++ b/src/main/java/graph/kruskalAlgorithm/Graph.java @@ -0,0 +1,240 @@ +package graph.kruskalAlgorithm; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class Graph { + + private List> allEdges; + private Map> allVertex; + boolean isDirected = false; + + public Graph(boolean isDirected) { + allEdges = new ArrayList>(); + allVertex = new HashMap>(); + this.isDirected = isDirected; + } + + public void addEdge(long id1, long id2) { + addEdge(id1, id2, 0); + } + + public void addVertex(Vertex vertex) { + if (allVertex.containsKey(vertex.getId())) { + return; + } + allVertex.put(vertex.getId(), vertex); + for (Edge edge : vertex.getEdges()) { + allEdges.add(edge); + } + } + + public Vertex addSingleVertex(long id) { + if (allVertex.containsKey(id)) { + return allVertex.get(id); + } + Vertex v = new Vertex(id); + allVertex.put(id, v); + return v; + } + + public Vertex getVertex(long id) { + return allVertex.get(id); + } + + public void addEdge(long id1, long id2, int weight) { + Vertex vertex1 = null; + if (allVertex.containsKey(id1)) { + vertex1 = allVertex.get(id1); + } else { + vertex1 = new Vertex(id1); + allVertex.put(id1, vertex1); + } + Vertex vertex2 = null; + if (allVertex.containsKey(id2)) { + vertex2 = allVertex.get(id2); + } else { + vertex2 = new Vertex(id2); + allVertex.put(id2, vertex2); + } + + Edge edge = new Edge(vertex1, vertex2, isDirected, weight); + allEdges.add(edge); + vertex1.addAdjacentVertex(edge, vertex2); + if (!isDirected) { + vertex2.addAdjacentVertex(edge, vertex1); + } + } + + public List> getAllEdges() { + return allEdges; + } + + public Collection> getAllVertex() { + return allVertex.values(); + } + + public void setDataForVertex(long id, T data) { + if (allVertex.containsKey(id)) { + Vertex vertex = allVertex.get(id); + vertex.setData(data); + } + } + + @Override + public String toString() { + StringBuffer buffer = new StringBuffer(); + for (Edge edge : getAllEdges()) { + buffer.append(edge.getVertex1() + " " + edge.getVertex2() + " " + edge.getWeight()); + buffer.append("\n"); + } + return buffer.toString(); + } +} + +class Vertex { + long id; + private T data; + private List> edges = new ArrayList<>(); + private List> adjacentVertex = new ArrayList<>(); + + Vertex(long id) { + this.id = id; + } + + public long getId() { + return id; + } + + public void setData(T data) { + this.data = data; + } + + public T getData() { + return data; + } + + public void addAdjacentVertex(Edge e, Vertex v) { + edges.add(e); + adjacentVertex.add(v); + } + + public String toString() { + return String.valueOf(id); + } + + public List> getAdjacentVertexes() { + return adjacentVertex; + } + + public List> getEdges() { + return edges; + } + + public int getDegree() { + return edges.size(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (int) (id ^ (id >>> 32)); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Vertex other = (Vertex) obj; + if (id != other.id) + return false; + return true; + } +} + +class Edge { + private boolean isDirected = false; + private Vertex vertex1; + private Vertex vertex2; + private int weight; + + Edge(Vertex vertex1, Vertex vertex2) { + this.vertex1 = vertex1; + this.vertex2 = vertex2; + } + + Edge(Vertex vertex1, Vertex vertex2, boolean isDirected, int weight) { + this.vertex1 = vertex1; + this.vertex2 = vertex2; + this.weight = weight; + this.isDirected = isDirected; + } + + Edge(Vertex vertex1, Vertex vertex2, boolean isDirected) { + this.vertex1 = vertex1; + this.vertex2 = vertex2; + this.isDirected = isDirected; + } + + Vertex getVertex1() { + return vertex1; + } + + Vertex getVertex2() { + return vertex2; + } + + int getWeight() { + return weight; + } + + public boolean isDirected() { + return isDirected; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((vertex1 == null) ? 0 : vertex1.hashCode()); + result = prime * result + ((vertex2 == null) ? 0 : vertex2.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Edge other = (Edge) obj; + if (vertex1 == null) { + if (other.vertex1 != null) + return false; + } else if (!vertex1.equals(other.vertex1)) + return false; + if (vertex2 == null) { + if (other.vertex2 != null) + return false; + } else if (!vertex2.equals(other.vertex2)) + return false; + return true; + } + + @Override + public String toString() { + return "Edge [isDirected=" + isDirected + ", vertex1=" + vertex1 + ", vertex2=" + vertex2 + ", weight=" + weight + + "]"; + } +} diff --git a/src/main/java/graph/kruskalAlgorithm/KruskalMST.java b/src/main/java/graph/kruskalAlgorithm/KruskalMST.java new file mode 100644 index 0000000..9e17fe4 --- /dev/null +++ b/src/main/java/graph/kruskalAlgorithm/KruskalMST.java @@ -0,0 +1,53 @@ +package graph.kruskalAlgorithm; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class KruskalMST { + + public List> getMST(Graph graph) { + List> allEdges = graph.getAllEdges(); + + Collections.sort(allEdges, (edge1, edge2) -> edge1.getWeight() <= edge2.getWeight() ? -1 : 1); + DisjointSet disjointSet = new DisjointSet(); + + for (Vertex vertex : graph.getAllVertex()) { + disjointSet.makeSet(vertex.getId()); + } + + List> resultEdge = new ArrayList>(); + for (Edge edge : allEdges) { + long root1 = disjointSet.findSet(edge.getVertex1().getId()); + long root2 = disjointSet.findSet(edge.getVertex2().getId()); + + if (root1 != root2) { + resultEdge.add(edge); + disjointSet.union(edge.getVertex1().getId(), edge.getVertex2().getId()); + } + } + return resultEdge; + } + + public static void main(String args[]) { + Graph graph = new Graph(false); + graph.addEdge(1, 2, 4); + graph.addEdge(1, 3, 1); + graph.addEdge(2, 5, 1); + graph.addEdge(2, 6, 3); + graph.addEdge(2, 4, 2); + graph.addEdge(6, 5, 2); + graph.addEdge(6, 4, 3); + graph.addEdge(4, 7, 2); + graph.addEdge(3, 4, 5); + graph.addEdge(3, 7, 8); + + KruskalMST mst = new KruskalMST(); + List> result = mst.getMST(graph); + int count = 0; + for (Edge edge : result) { + count += edge.getWeight(); + System.out.println(edge.getVertex1() + " " + edge.getVertex2() + "-> " + count); + } + } +} \ No newline at end of file diff --git a/src/main/java/graph/leetcode/AccountsMerge.java b/src/main/java/graph/leetcode/AccountsMerge.java new file mode 100644 index 0000000..b76e68d --- /dev/null +++ b/src/main/java/graph/leetcode/AccountsMerge.java @@ -0,0 +1,132 @@ +package graph.leetcode; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * tricky union find + * + * https://www.youtube.com/watch?v=QHniHFvxAl8&ab_channel=ShiranAfergan + * https://leetcode.com/problems/accounts-merge/ + * + * Here, we use disjoint set union data structure to keep track of same user accounts. + * We use a hash map to map emails to account's indices (index of the account in accounts list). + * Note that different accounts (equivalently account ids) may belong to the same user. + * We perform union operation on account ids. This might reduce the number of union operations, compared to when we perform union on all emails. + * We start by iterating all email ids linked to all the accounts. + * If we observe that an email has been observed before, we know that both email ids must belong to different accounts which belong to the same user. + * Thus, we fetch the account ids corresponding to this email id, one is the current account's index, and the other is the account index from the map. + * We then perform union on the account indices. + * + * In the end, all account ids which belong to the same user get grouped together and return the same account id on calling findSet. + * For each email, we check which account id it belongs to, get that account's parent's id from the union structure, and add the email to that id in a new map. + * + * a b c // now b, c have parent a + * d e f // now e, f have parent d + * g a d // now abc, def all merged to group g + * + * parents populated after parsing 1st account: a b c + * a->a + * b->a + * c->a + * + * parents populated after parsing 2nd account: d e f + * d->d + * e->d + * f->d + * + * parents populated after parsing 3rd account: g a d + * g->g + * a->g + * d->g + */ +public class AccountsMerge { + public List> accountsMerge(List> accounts) { + + UnionFind uf = new UnionFind(accounts.size()); + + Map emailsToIdMapper = new TreeMap<>(); + + + for (int i = 0; i < accounts.size(); i++) { + + List emails = accounts.get(i); + // Step 1: traverse all emails except names, if we have not seen an email before, put it with its index into map. + // Otherwise, union the email to its parent index. + // we are starting j=1 because j=0 will contain the name not the email + for(int j=1;j> result = new HashMap<>(); + + for (String email : emailsToIdMapper.keySet()) { + + int id = emailsToIdMapper.get(email); + + int parentId = uf.find(id); + if (!result.containsKey(parentId)) { + result.putIfAbsent(parentId, new ArrayList<>()); + result.get(parentId).add(accounts.get(parentId).get(0)); + } + + result.get(parentId).add(email); + } + + return new ArrayList<>(result.values()); + } + + static class UnionFind { + int[] parent; + int[] rank; + + public UnionFind(int n) { + this.parent = new int[n]; + this.rank = new int[n]; + + for (int i = 0; i < n; i++) { + this.parent[i] = i; + } + } + + public boolean union(int x, int y) { + + int parentX = find(x); + int parentY = find(y); + + if (parentX == parentY) return false; + + if (rank[parentX] > rank[parentY]) { + parent[parentY] = parentX; + } else if (rank[parentY] > rank[parentX]) { + parent[parentX] = parentY; + } else { + parent[parentY] = parentX; + rank[parentX]++; + } + + return true; + } + + public int find(int x) { + if (parent[x] == x) return x; + + parent[x] = find(parent[x]); + return parent[x]; + } + + public boolean isSameComponent(int p, int q) { + return find(p) == find(q); + } + } +} \ No newline at end of file diff --git a/src/main/java/graph/leetcode/AlienDictionary.java b/src/main/java/graph/leetcode/AlienDictionary.java new file mode 100644 index 0000000..23ce21e --- /dev/null +++ b/src/main/java/graph/leetcode/AlienDictionary.java @@ -0,0 +1,110 @@ +package graph.leetcode; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; + +/** + * tricky topological sort + * + * https://leetcode.com/problems/alien-dictionary + * + * Really nice solution! Let me try to explain the code with example in the problem description: + * + * First, build a degree map for each character in all the words: + * + * w:0 + * r:0 + * t:0 + * f:0 + * e:0 + * + * Then build the hashmap by comparing the adjacent words, the first character that is different between two adjacent words reflect the lexicographical order. For example: + * + * "wrt", + * "wrf", + * first different character is 3rd letter, so t comes before f + * + * "wrf", + * "er", + * first different character is 1rd letter, so w comes before e + * + * The characters in set come after the key. x->y means letter x comes before letter y. x -> set: y,z,t,w means x comes before all the letters in the set. The final HashMap "map" looks like. + * + * t -> set: f + * w -> set: e + * r -> set: t + * e -> set: r + * + * and final HashMap "degree" looks like, the number means "how many letters come before the key": + * + * w:0 + * r:1 + * t:1 + * f:1 + * e:1 + * + * Then use Kahn's algorithm to do topological sort. This is essentially BFS. + */ + +public class AlienDictionary { + public String alienOrder(String[] words) { + // Step 0: Create data structures and find all unique letters. + Map> adjList = new HashMap<>(); + Map counts = new HashMap<>(); + for (String word : words) { + for (char c : word.toCharArray()) { + counts.put(c, 0); + adjList.put(c, new ArrayList<>()); + } + } + + // Step 1: Find all edges. + for (int i = 0; i < words.length - 1; i++) { + String word1 = words[i]; + String word2 = words[i + 1]; + // Check that word2 is not a prefix of word1. + // One edge case is when the second word is the prefix of the first word, for example: ["abc", "ab"] + // Because the prefix should always be at the front. + // check for cases like, ["wrtkj","wrt"]; it's invalid, because this input is not in sorted lexicographical order + if (word1.length() > word2.length() && word1.startsWith(word2)) { + return ""; + } + // Find the first non match and insert the corresponding relation. + for (int j = 0; j < Math.min(word1.length(), word2.length()); j++) { + if (word1.charAt(j) != word2.charAt(j)) { + adjList.get(word1.charAt(j)).add(word2.charAt(j)); + counts.put(word2.charAt(j), counts.get(word2.charAt(j)) + 1); + break; + } + } + } + + // Step 2: Breadth-first search. + StringBuilder sb = new StringBuilder(); + Queue queue = new LinkedList<>(); + for (Character c : counts.keySet()) { + if (counts.get(c)==0) { + queue.add(c); + } + } + while (!queue.isEmpty()) { + Character c = queue.remove(); + sb.append(c); + for (Character next : adjList.get(c)) { + counts.put(next, counts.get(next) - 1); + if (counts.get(next)==0) { + queue.add(next); + } + } + } + + if (sb.length() < counts.size()) { + return ""; + } + return sb.toString(); + } +} diff --git a/src/main/java/graph/leetcode/AllPathsInGraph.java b/src/main/java/graph/leetcode/AllPathsInGraph.java new file mode 100644 index 0000000..9b2d336 --- /dev/null +++ b/src/main/java/graph/leetcode/AllPathsInGraph.java @@ -0,0 +1,49 @@ +package graph.leetcode; + +import java.util.ArrayList; +import java.util.List; + +/** + * tricky graph traversal + * + * Given a directed, acyclic graph of N nodes. Find all possible paths from node 0 to node N-1, and return them in any order. + * + * The graph is given as follows: the nodes are 0, 1, ..., graph.length - 1. + * graph[i] is a list of all nodes j for which the edge (i, j) exists. + * + * Example: + * Input: [[1,2], [3], [3], []] + * Output: [[0,1,3],[0,2,3]] + * Explanation: The graph looks like this: + * 0--->1 + * | | + * v v + * 2--->3 + * There are two paths: 0 -> 1 -> 3 and 0 -> 2 -> 3. + */ +public class AllPathsInGraph { + public List> allPathsSourceTarget(int[][] graph) { + boolean[] visited=new boolean[graph.length]; + List path=new ArrayList<>(); + List> ans=new ArrayList<>(); + visited[0]=true; + path.add(0); + dfs(graph,visited,0,path, ans); + return ans; + } + public void dfs(int[][] graph,boolean[] visited,int u,List path, List> ans){ + if(u==graph.length-1){ + ans.add(new ArrayList<>(path)); + return; + } + for(int i=0;i (5, 6, 2, 0) + * + * Now when i = 1, curr = 4 + * component is => (1, 4) + * + * i = 2, this number is already used in a component + * i = 3 already used + * i = 4 already used...... and so on + * + * so we have two components + * (5, 6, 2, 0) and (1, 4) + * + * The largest size component is (5, 6, 2, 0) + * Hence out ans for this test case is 4. + */ +public class ArrayNesting { + + public int arrayNesting(int[] nums) { + boolean[] visited = new boolean[nums.length]; + int result = 0; + for (int i = 0; i < nums.length; i++) { + if (!visited[i]) { + int start = i; + int count = 0; + while (!visited[start]) { + count++; + visited[start] = true; + start = nums[start]; + } + if (result < count) { + result = count; + } + } + } + return result; + } + + public int arrayNestingDFS(int[] nums) { + int max = Integer.MIN_VALUE; + + for (int i = 0; i < nums.length; i++) { + if (nums[i]<0) + continue; + max = Math.max(max, calcLength(nums, i)); + } + return max; + } + + private int calcLength(int[] nums, int start) { + + if(start<0 || start==nums.length || nums[start]<0){ + return 0; + } + + int nextValue = nums[start]; + nums[start] = Integer.MIN_VALUE; + + return 1 + calcLength(nums, nextValue); + + } +} diff --git a/src/main/java/graph/leetcode/BombEnemy.java b/src/main/java/graph/leetcode/BombEnemy.java new file mode 100644 index 0000000..bc99863 --- /dev/null +++ b/src/main/java/graph/leetcode/BombEnemy.java @@ -0,0 +1,42 @@ +package graph.leetcode; + +/** + * https://leetcode.com/problems/bomb-enemy/ + */ +public class BombEnemy { + + private int row; + private int col; + private int max = 0; + private int[][] dirs = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + + + public int maxKilledEnemies(char[][] grid) { + if (grid == null || grid.length == 0) + return 0; + row = grid.length; + col = grid[0].length; + for (int i = 0; i < row; i++) + for (int j = 0; j < col; j++) + if (grid[i][j] == '0') + max = Math.max(max, bfs(grid, i, j)); + return max; + } + + private int bfs(char[][] grid, int i, int j) { + int count = 0; + for (int[] dir : dirs) { + int x = i + dir[0]; + int y = j + dir[1]; + + while (x < row && y < col && x >= 0 && y >= 0 && grid[x][y] != 'W') { + if (grid[x][y] == 'E') + count++; + x = x + dir[0]; + y = y + dir[1]; + } + } + return count; + + } +} diff --git a/src/main/java/graph/leetcode/CanVisitAllRooms.java b/src/main/java/graph/leetcode/CanVisitAllRooms.java new file mode 100644 index 0000000..0835ff8 --- /dev/null +++ b/src/main/java/graph/leetcode/CanVisitAllRooms.java @@ -0,0 +1,42 @@ +package graph.leetcode; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * https://leetcode.com/problems/keys-and-rooms/ + * Input: rooms = [[1],[2],[3],[]] + * Output: true + * Explanation: + * We visit room 0 and pick up key 1. + * We then visit room 1 and pick up key 2. + * We then visit room 2 and pick up key 3. + * We then visit room 3. + * Since we were able to visit every room, we return true. + */ +public class CanVisitAllRooms { + + public boolean canVisitAllRooms(List> rooms) { + Set set = new HashSet<>(); + + set.add(0); + Deque stack = new ArrayDeque<>(); + stack.push(0); + while (!stack.isEmpty()) { + int i = stack.pop(); + + for (int keys : rooms.get(i)) { + if (!set.contains(keys)) { + set.add(keys); + stack.push(keys); + } + if (set.size() == rooms.size()) return true; + } + } + + return set.size() == rooms.size(); + } +} diff --git a/src/main/java/graph/leetcode/CheapestFlightKStops.java b/src/main/java/graph/leetcode/CheapestFlightKStops.java new file mode 100644 index 0000000..eb6c371 --- /dev/null +++ b/src/main/java/graph/leetcode/CheapestFlightKStops.java @@ -0,0 +1,104 @@ +package graph.leetcode; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.PriorityQueue; + +/** + * https://leetcode.com/problems/cheapest-flights-within-k-stops/ + */ +public class CheapestFlightKStops { + +// For example consider the following graph. Let source ‘u’ be vertex 0, destination ‘v’ be 3 and k be 2. +// There are two walks of length 2, the walks are {0, 2, 3} and {0, 1, 3}. +// The shortest among the two is {0, 2, 3} and weight of path is 3+6 = 9. + + public int findCheapestPrice(int n, int[][] flights, int src, int dst, int k) { + + // K doesn't include destination, because that's how it is for flights, hence K++ to account a hop for destination + k++; + Map> adjList = new HashMap<>(); + + for (int[] flight : flights) { + adjList.computeIfAbsent(flight[0], x -> new ArrayList<>()).add(new int[]{flight[1], flight[2]}); + } + + int[] costs = new int[n]; + int[] stops = new int[n]; + + Arrays.fill(costs,Integer.MAX_VALUE); + Arrays.fill(stops,Integer.MAX_VALUE); + + PriorityQueue queue = new PriorityQueue<>(Comparator.comparingInt(a -> a[1])); + + costs[src]=0; + stops[src]=0; + + queue.offer(new int[]{src,0,0}); + + while(!queue.isEmpty()){ + + int[] node = queue.poll(); + int current = node[0], cost = node[1], stop = node[2]; + + + if(current==dst) return cost; + + + if(stop==k) continue; + + for(int[] adj: adjList.getOrDefault(current,new ArrayList<>())){ + + int next = adj[0]; + int costNext = adj[1]; + + // Add for better cost, or stop + if (cost + costNext < costs[next] || stop + 1 < stops[next]) { + queue.offer(new int[]{next, cost + costNext, stop + 1}); + costs[next] = cost + costNext; + stops[next] = stop + 1; + } + } + + } + return -1; + } + + /** + * // BELLMON FORD: Relax all edges |V| - 1 times. A simple shortest path from src to any other vertex can have + * // at-most |V| - 1 edges since there will not be any multiple flights between two cities. + * // Here we will relax K times. + * // Much like BFS, run the algorithm K times, if the answer exists, it should be stored in the helper matrix + * // O(K⋅E) + */ + public int findCheapestPriceBellman(int n, int[][] flights, int src, int dst, int k) { + int[] cheapestPrices = new int[n]; + Arrays.fill(cheapestPrices, Integer.MAX_VALUE); + cheapestPrices[src] = 0; + + for (int i = 0; i < k + 1; i++) { // k = 1 means # edges <= 2 + + // Copy to ensure one vertex doesn't go through multiple relaxation in same iteration + // If there was not a limit on K stops (intermediary nodes), then we DO NOT need copy + // other way would be to use 2D array to track K as well which won't require copy. + int[] prevCheapestPrices = cheapestPrices.clone(); + + for (int[] flight : flights) { + int source = flight[0]; + int dest = flight[1]; + int price = flight[2]; + + if (prevCheapestPrices[source] != Integer.MAX_VALUE) { + cheapestPrices[dest] = Math.min(cheapestPrices[dest], prevCheapestPrices[source] + price); + } + } + } + + return cheapestPrices[dst] == Integer.MAX_VALUE ? -1 : cheapestPrices[dst]; + } + +} diff --git a/src/main/java/graph/leetcode/CloneGraph.java b/src/main/java/graph/leetcode/CloneGraph.java new file mode 100644 index 0000000..ed9123b --- /dev/null +++ b/src/main/java/graph/leetcode/CloneGraph.java @@ -0,0 +1,58 @@ +package graph.leetcode; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Queue; + +/** + * https://leetcode.com/problems/clone-graph/ + */ +public class CloneGraph { + private static class Node { + public int val; + public List neighbors; + + public Node() { + val = 0; + neighbors = new ArrayList(); + } + + public Node(int _val) { + val = _val; + neighbors = new ArrayList(); + } + + public Node(int _val, ArrayList _neighbors) { + val = _val; + neighbors = _neighbors; + } + } + + public Node cloneGraph(Node node) { + if (node == null) { + return null; + } + Map cloned = new HashMap<>(); + Queue queue = new ArrayDeque<>(); + queue.offer(node); + Node clonedNode = new Node(node.val); + cloned.put(node, clonedNode); + while (!queue.isEmpty()) { + Node curr = queue.poll(); + for (Node nb : curr.neighbors) { + if (!cloned.containsKey(nb)) { + queue.offer(nb); + cloned.put(nb, new Node(nb.val)); + } + cloned.get(curr).neighbors.add(cloned.get(nb)); + } + } + return clonedNode; + + } + + +} \ No newline at end of file diff --git a/src/main/java/graph/leetcode/ClosedIsland.java b/src/main/java/graph/leetcode/ClosedIsland.java new file mode 100644 index 0000000..31eadef --- /dev/null +++ b/src/main/java/graph/leetcode/ClosedIsland.java @@ -0,0 +1,49 @@ +package graph.leetcode; + +/** + * https://leetcode.com/problems/number-of-closed-islands/ + */ +public class ClosedIsland { + + public int closedIsland(int[][] grid) { + + int VISITED = 2; + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[0].length; j++) { + + if (i == 0 || j == 0 || i == grid.length - 1 || j == grid[0].length - 1) { + if (grid[i][j] == 0) { + dfs(grid, i, j, VISITED); + } + } + } + } + + int result = 0; + + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[0].length; j++) { + + if (grid[i][j] == 0) { + dfs(grid, i, j, VISITED); + result++; + } + } + } + + return result; + } + + public void dfs(int[][] grid, int i, int j, int VISITED) { + if (i < 0 || j < 0 || j >= grid[0].length || i >= grid.length) return; + + if (grid[i][j] == 1 || grid[i][j] == VISITED) return; + + grid[i][j] = VISITED; + + dfs(grid, i + 1, j, VISITED); + dfs(grid, i, j + 1, VISITED); + dfs(grid, i - 1, j, VISITED); + dfs(grid, i, j - 1, VISITED); + } +} diff --git a/src/main/java/graph/leetcode/ConnectCities.java b/src/main/java/graph/leetcode/ConnectCities.java new file mode 100644 index 0000000..7e49dca --- /dev/null +++ b/src/main/java/graph/leetcode/ConnectCities.java @@ -0,0 +1,68 @@ +package graph.leetcode; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +/** + * Input: N = 3, connections = [[1,2,5],[1,3,6],[2,3,1]] + * Output: 6 + * Explanation: + * Choosing any 2 edges will connect all cities so we choose the minimum 2. + */ +public class ConnectCities { + public int solve(int A, ArrayList> B) { + + // greedy-ly we're looking from low-cost roads to minimise the cost + B.sort((Comparator.comparingInt(a -> a.get(2)))); + + UnionSet us = new UnionSet(A); + int result = 0; + for (List row : B) { + if (us.union(row.get(0), row.get(1))) { + result += row.get(2); + } + } + return result; + } + + static class UnionSet { + int[] parent; + int[] rank; + int count; + + public UnionSet(int n) { + this.count = count; + this.parent = new int[n + 1]; + this.rank = new int[n + 1]; + for (int i = 0; i <= n; i++) { + this.parent[i] = i; + this.rank[i] = 1; + } + } + + public int find(int n) { + if (parent[n] == n) return n; + parent[n] = find(parent[n]); + return parent[n]; + } + + public boolean union(int x, int y) { + int rootX = find(x); + int rootY = find(y); + if (rootX == rootY) return false; + + if (rank[rootX] < rank[rootY]) { + parent[rootX] = rootY; + } else if (rank[rootX] > rank[rootY]) { + parent[rootY] = rootX; + } else { + parent[rootY] = rootX; + rank[rootX]++; + } + count--; + return true; + } + } +} diff --git a/src/main/java/graph/leetcode/ConnectCitiesWithDiscount.java b/src/main/java/graph/leetcode/ConnectCitiesWithDiscount.java new file mode 100644 index 0000000..74931a6 --- /dev/null +++ b/src/main/java/graph/leetcode/ConnectCitiesWithDiscount.java @@ -0,0 +1,73 @@ +package graph.leetcode; + +import java.util.ArrayList; +import java.util.Comparator; +import java.util.List; +import java.util.PriorityQueue; + +/** + * revise + * https://leetcode.com/problems/minimum-cost-to-reach-city-with-discounts + * highways[i] = [city1i, city2i, tolli] + * + * Input: n = 5, highways = [[0,1,4],[2,1,3],[1,4,11],[3,2,3],[3,4,2]], discounts = 1 + * Output: 9 + * Explanation: + * Go from 0 to 1 for a cost of 4. + * Go from 1 to 4 and use a discount for a cost of 11 / 2 = 5. + * The minimum cost to go from 0 to 4 is 4 + 5 = 9. + */ +public class ConnectCitiesWithDiscount { + + /** + * Dijkstra is used to solve single source, positive weight and minimum path questions like this. + * The only trick here is to use 2D visited array to check and do the pruning work when the next city is put into the PriorityQueue, + * one for city and one for the amount of discounts used. + * The principle of Dijkstra Algorithm determines the fact that the first time the destination is reached, it will be the optimal solution. + */ + public int minimumCost(int n, int[][] highways, int discounts) { + + List[] graph = new List[n]; + for (int i = 0; i < n; i++) graph[i] = new ArrayList<>(); + + for (int[] x : highways) { + int cityA = x[0], cityB = x[1], cost = x[2]; + graph[cityA].add(new int[]{cityB, cost}); + graph[cityB].add(new int[]{cityA, cost}); + } + + PriorityQueue pq = new PriorityQueue<>(Comparator.comparingInt(o -> o[0])); + pq.offer(new int[]{0, 0, 0}); // {cost, node, discount} + boolean[] visited = new boolean[n]; + + /** + * # Because of how djikstra works, when we reach this node the first time, + * # we will reach there with the lowest cost. However, we may reach this node + * # again with a highest cost, but more discount tickets, which can lead to a + * # more optimal soln at the end. If we ever come back to this node with the same or + * # fewer discounts, the soln is not optimal. + */ + while (!pq.isEmpty()) { + int[] cur = pq.poll(); + int cost = cur[0], city = cur[1], dis = cur[2]; + + /* We dont have to push it back into the heap, we have already found the shortest path to this city. + this holds true as we storing the cost from source in the minHeap, this would not be valid if we are just storing the curr cost + */ + visited[city] = true; + + if (city == n - 1) return cost; + + for (int[] x : graph[city]) { + int next = x[0], weight = x[1]; + if (!visited[next]) { + pq.offer(new int[]{cost + weight, next, dis}); + if (dis < discounts) { + pq.offer(new int[]{cost + weight / 2, next, dis + 1}); + } + } + } + } + return -1; + } +} diff --git a/src/main/java/graph/leetcode/ConnectedComponentsInGraph.java b/src/main/java/graph/leetcode/ConnectedComponentsInGraph.java new file mode 100644 index 0000000..ce312ef --- /dev/null +++ b/src/main/java/graph/leetcode/ConnectedComponentsInGraph.java @@ -0,0 +1,32 @@ +package graph.leetcode; + +public class ConnectedComponentsInGraph { + + public int countComponents(int n, int[][] edges) { + int[] roots = new int[n]; + + for (int i = 0; i < n; i++) { + roots[i] = i; + } + + for (int[] edge : edges) { + int r1 = find(roots, edge[0]); + int r2 = find(roots, edge[1]); + + if (r1 != r2) { + roots[r1] = r2; + n--; + } + } + + return n; + } + + private int find(int[] roots, int key) { + while (roots[key] != key) { + key = roots[key]; + } + + return key; + } +} diff --git a/src/main/java/graph/leetcode/CourseSchedule.java b/src/main/java/graph/leetcode/CourseSchedule.java new file mode 100644 index 0000000..23ebc6b --- /dev/null +++ b/src/main/java/graph/leetcode/CourseSchedule.java @@ -0,0 +1,170 @@ +package graph.leetcode; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.Set; + +/** + * Input: numCourses = 2, prerequisites = [[1,0]] + * Output: true + * Explanation: There are a total of 2 courses to take. + * To take course 1 you should have finished course 0. So it is possible. + *

+ * Input: numCourses = 2, prerequisites = [[1,0],[0,1]] + * Output: false + * Explanation: There are a total of 2 courses to take. + * To take course 1 you should have finished course 0, and to take course 0 you should + * also have finished course 1. So it is impossible. + */ +class CourseSchedule { + public boolean canFinish(int numCourses, int[][] prerequisites) { + + Map> map = new HashMap<>(); // Courses that depend on the key + int[] indegree = new int[numCourses]; // # of prerequisites for course i + Queue queue = new ArrayDeque<>(); // Used to find dependants and decrease their outdegree + + for (int[] pre : prerequisites) { + map.getOrDefault(pre[1], new ArrayList<>()).add(pre[0]); + indegree[pre[0]]++; + } + + for (int i = 0; i < indegree.length; i++) { + if (indegree[i] == 0) { + queue.offer(i); + } + } + + int count = 0; + while (!queue.isEmpty()) { + int temp = queue.poll(); + if (indegree[temp] == 0) { + count++; // if cond for duplicates + } + if (!map.containsKey(temp)) { + continue; + } + for (int i : map.get(temp)) { + if (--indegree[i] == 0) { + queue.offer(i); + } + } + } + + return count == numCourses; + } + + public List checkIfPrerequisite(int numCourses, int[][] prerequisites, int[][] queries) { + int[] indegree = new int[numCourses]; + Map> adj = new HashMap<>(); + Map> prerequisitesMap = new HashMap<>(); + + for (int i = 0; i < numCourses; i++) { + prerequisitesMap.put(i, new HashSet<>()); + adj.put(i, new HashSet<>()); + } + + for (int[] pre : prerequisites) { + int dst = pre[0]; + int src = pre[1]; + + indegree[dst]++; + adj.get(src).add(dst); + } + + Queue q = new LinkedList<>(); + for (int i = 0; i < numCourses; i++) { + if (indegree[i] == 0) { + q.offer(i); + } + } + + while (!q.isEmpty()) { + int node = q.poll(); + Set adjset = adj.get(node); + for (int depcrs : adjset) { + prerequisitesMap.get(depcrs).add(node); + prerequisitesMap.get(depcrs).addAll(prerequisitesMap.get(node)); + indegree[depcrs]--; + if (indegree[depcrs] == 0) { + q.offer(depcrs); + } + } + } + + List rslt = new ArrayList<>(); + for (int[] qry : queries) { + Set pset = prerequisitesMap.get(qry[0]); + if (pset.contains(qry[1])) { + rslt.add(true); + } else { + rslt.add(false); + } + } + return rslt; + } + + public boolean canFinishDFS(int numCourses, int[][] prerequisites) { + // this method basically finds a back-edge between nodes + // backedge is when doing a node(A)'s dfs, it puts A to a temp state + // while traversing A's child, if any of child dosen't have anymore child it's marked as completed + // if there are children it put's the current child to temp state and visits it's children + // so when doing a dfs for a node if it encounters a temp state node rather than completed node + // then that means there's a cycle we cannot complete the course + // (T) A \ + // / / + // (T) B / + // / \ / + // (Co) C D (T) while doing DFS for D's components we encounter A, but A is still in temp state + // + + ArrayList[] adjList = new ArrayList[numCourses]; + + for (int i = 0; i < numCourses; i++) { + adjList[i] = new ArrayList<>(); + } + for (int[] ints : prerequisites) { + adjList[ints[0]].add(ints[1]); + } + int[] color = new int[numCourses]; + + for (int i = 0; i < numCourses; i++) { + if (color[i] != 2 && dfs(adjList, i, color)) return false; + } + return true; + + } + + public boolean dfs(ArrayList[] al, int curr, int[] color) { + if (color[curr] == 1) return true; + color[curr] = 1; + for (int x : al[curr]) { + if (color[x] != 2 && dfs(al, x, color)) + return true; + } + color[curr] = 2; + return false; + } + + + // this is to get the order of course as output + public boolean dfs(List[] adjList, int[] visited, List result, int node) { + if (visited[node] == 1) return false; + if (visited[node] == 2) return true; + + visited[node] = 1; + for (int adj : adjList[node]) { + if (!dfs(adjList, visited, result, adj)) { + return false; + } + } + visited[node] = 2; + result.add(node); // this will keep track of which to fininsh first and last + return true; + } +} \ No newline at end of file diff --git a/src/main/java/graph/leetcode/CourseScheduleII.java b/src/main/java/graph/leetcode/CourseScheduleII.java new file mode 100644 index 0000000..fc491a5 --- /dev/null +++ b/src/main/java/graph/leetcode/CourseScheduleII.java @@ -0,0 +1,211 @@ +package graph.leetcode; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.Set; + +/** + * Input: numCourses = 2, prerequisites = [[1,0]] + * Output: true + * Explanation: There are a total of 2 courses to take. + * To take course 1 you should have finished course 0. So it is possible. + *

+ * Input: numCourses = 2, prerequisites = [[1,0],[0,1]] + * Output: false + * Explanation: There are a total of 2 courses to take. + * To take course 1 you should have finished course 0, and to take course 0 you should + * also have finished course 1. So it is impossible. + */ +public class CourseScheduleII { + public boolean canFinish(int numCourses, int[][] prerequisites) { + + Map> map = new HashMap<>(); // Courses that depend on the key + int[] indegrees = new int[numCourses]; // # of prerequisites for course i + Queue queue = new ArrayDeque<>(); // Used to find dependants and decrease their outdegree + + for (int[] pre : prerequisites) { + map.getOrDefault(pre[1], new ArrayList<>()).add(pre[0]); + indegrees[pre[0]]++; + } + + for (int i = 0; i < indegrees.length; i++) { + if (indegrees[i] == 0) { + queue.offer(i); + } + } + + int count = 0; + while (!queue.isEmpty()) { + int temp = queue.poll(); + if (indegrees[temp] == 0) { + count++; // if cond for duplicates + } + if (!map.containsKey(temp)) { + continue; + } + for (int i : map.get(temp)) { + if (--indegrees[i] == 0) { + queue.offer(i); + } + } + } + + return count == numCourses; + } + + public int[] findOrder(int numCourses, int[][] prerequisites) { + + Map> adjList = new HashMap<>(); + int[] indegree = new int[numCourses]; + int[] topologicalOrder = new int[numCourses]; + + // Create the adjacency list representation of the graph + for (int[] prerequisite : prerequisites) { + int dest = prerequisite[0]; + int src = prerequisite[1]; + adjList.computeIfAbsent(src, x -> new ArrayList<>()).add(dest); + indegree[dest] += 1; + } + + // Add all vertices with 0 in-degree to the queue + Queue q = new LinkedList<>(); + for (int i = 0; i < numCourses; i++) { + if (indegree[i] == 0) { + q.add(i); + } + } + + int i = 0; + // Process until the Q becomes empty + while (!q.isEmpty()) { + int node = q.remove(); + topologicalOrder[i++] = node; + + // Reduce the in-degree of each neighbor by 1 + if (adjList.containsKey(node)) { + for (Integer neighbor : adjList.get(node)) { + indegree[neighbor]--; + + // If in-degree of a neighbor becomes 0, add it to the Q + if (indegree[neighbor] == 0) { + q.add(neighbor); + } + } + } + } + + return i == numCourses ? topologicalOrder : new int[0]; + } + + public List checkIfPrerequisite(int numCourses, int[][] prerequisites, int[][] queries) { + int[] indegree = new int[numCourses]; + Map> adj = new HashMap<>(); + Map> prerequisitesMap = new HashMap<>(); + + for (int i = 0; i < numCourses; i++) { + prerequisitesMap.put(i, new HashSet<>()); + adj.put(i, new HashSet<>()); + } + + for (int[] pre : prerequisites) { + int dst = pre[0]; + int src = pre[1]; + + indegree[dst]++; + adj.get(src).add(dst); + } + + Queue q = new LinkedList<>(); + for (int i = 0; i < numCourses; i++) { + if (indegree[i] == 0) { + q.offer(i); + } + } + + while (!q.isEmpty()) { + int node = q.poll(); + Set adjset = adj.get(node); + for (int depcrs : adjset) { + prerequisitesMap.get(depcrs).add(node); + prerequisitesMap.get(depcrs).addAll(prerequisitesMap.get(node)); + indegree[depcrs]--; + if (indegree[depcrs] == 0) { + q.offer(depcrs); + } + } + } + + List rslt = new ArrayList<>(); + for (int[] qry : queries) { + Set pset = prerequisitesMap.get(qry[0]); + if (pset.contains(qry[1])) { + rslt.add(true); + } else { + rslt.add(false); + } + } + return rslt; + } + + public boolean canFinishDFS(int numCourses, int[][] prerequisites) { + // this method basically finds a back-edge between nodes + // backedge is when doing a node(A)'s dfs, it puts A to a temp state + // while traversing A's child, if any of child dosen't have anymore child it's marked as completed + // if there are children it put's the current child to temp state and visits it's children + // so when doing a dfs for a node if it encounters a temp state node rather than completed node + // then that means there's a cycle we cannot complete the course + // (T) A \ + // / / + // (T) B / + // / \ / + // (Co) C D (T) while doing DFS for D's components we encounter A, but A is still in temp state + // + + ArrayList[] adjList = new ArrayList[numCourses]; + + for (int i = 0; i < numCourses; i++) { + adjList[i] = new ArrayList<>(); + } + for (int[] ints : prerequisites) { + adjList[ints[0]].add(ints[1]); + } + int[] color = new int[numCourses]; + + for (int i = 0; i < numCourses; i++) { + if (color[i] != 2 && dfs(adjList, i, color)) return false; + } + return true; + + } + + public boolean dfs(ArrayList[] al, int curr, int[] color) { + if (color[curr] == 1) return true; + color[curr] = 1; + for (int x : al[curr]) if (color[x] != 2 && dfs(al, x, color)) return true; + color[curr] = 2; + return false; + } + + + // this is to get the order of course as output + public boolean dfs(List[] adjList, int[] visited, List result, int node) { + if (visited[node] == 1) return false; + if (visited[node] == 2) return true; + + visited[node] = 1; + for (int adj : adjList[node]) { + if (!dfs(adjList, visited, result, adj)) { + return false; + } + } + visited[node] = 2; + result.add(node); // this will keep track of which to fininsh first and last + return true; + } +} \ No newline at end of file diff --git a/src/main/java/graph/leetcode/CriticalConnectionInGraph.java b/src/main/java/graph/leetcode/CriticalConnectionInGraph.java new file mode 100644 index 0000000..1090d94 --- /dev/null +++ b/src/main/java/graph/leetcode/CriticalConnectionInGraph.java @@ -0,0 +1,86 @@ +package graph.leetcode; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * TODO revise + * Idea: An connection is critical if and only if it is not in a cycle. + * In other words, a connection (u, v) (u is parent of v in the DFS path) is critical if there is no way to reach u from v or the descendants of v. + * + * Use an array 'visitedAt[]' to store the timestamps when each node is visited. + * For each node, we will find the smallest 'visitedAt' of all its descendants. + * 2.1. If the smallest 'visitedAt' < current node's 'visitedAt', the node must be in a cycle since the smallest 'visitedAt' must be the 'visitedAt' of an ancestor of the current node. + * 2.2. If the smallest 'visitedAt' > the current node's 'visitedAt', the node must not be in a cycle, and the connection from its parent to it is critical. + * 2.3. If the smallest 'visitedAt' == the current node's 'visitedAt' the node must be in a cycle. However, + * If the current node has a valid parent, the connection from its parent to it is critical. For example, node 3 in n = 6 and connections = [[0,1],[1,2],[2,0],[1,3],[3,4],[4,5],[5,3]]. + * If the current node does not have a valid parent, it's not a node of a critical connection. For example, node 0 in n = 3 and connections = [[0,1], [1,2], [2,0]]. + * + * Complexity: + * Time = O(graph) + O(DFS) = O(|E| + |V|) + O(|E| + |V|) = O(|E| + |V|) + * Space = O(graph) + O(visitedAt) + O(DFS) = O(|E| + |V|) + O(|V|) + O(|V|) = O(|V| + |E|) + */ +public class CriticalConnectionInGraph { + int startTime=1; + public List> criticalConnections(int n, List> connections) { + if (connections == null) { + return Collections.emptyList(); + } + + List> criticalConns = new ArrayList<>(); + dfs(buildGraph(n, connections), new int[n], 0, -1, criticalConns); // -1 is a dummy parent of the server 0 + return criticalConns; + } + + /** + * The dfs method is basically trying to find the node with the minimum timestamp the current node is able to reach. + * By calling the dfs method with all of the current node's neighbors, + * we get a number of results representing the timestamps the current node can reach and we choose the minimal value from these results. + * Our purpose is to find out the cycle and the minimal value is used to do so. Taking a step back, if there is a cycle existing in the graph, + * we will eventually go back to a previous timestamp through the cycle; in another word, we will find a node that can reach to a previous timestamp. + * When we find such node, we will return the previous timestamp as the result of the dfs method and the caller (the parent node) + * who is calling the dfs method will get the previous timestamp as well. + * The minimal value among the results of the dfs invocations for a node's all child nodes will be larger than the previous timestamp + * until we get back to the beginning of the loop (technically there is no beginning of a loop but the beginning here is referring to the node that firstly detected the cycle). + * All the connections within a loop are not critical connections; in another word, we just need to return all the connections outside of any loops. + */ + private int dfs(List> graph, int[] visitedAt, int server, int parent, List> criticalConns) { + if (visitedAt[server] > 0) { // return immediately if a node has been visited before + return visitedAt[server]; + } + + visitedAt[server] = startTime++; + + int minVisitedAtOfAllNeighbors = Integer.MAX_VALUE; + for (int neighbor : graph.get(server)) { + if (neighbor == parent) { // skip parent as we only want to explore down, not up + continue; + } + + int neighborVisitedAt = dfs(graph, visitedAt, neighbor, server, criticalConns); + minVisitedAtOfAllNeighbors = Math.min(minVisitedAtOfAllNeighbors, neighborVisitedAt); + } + + if (visitedAt[server] <= minVisitedAtOfAllNeighbors && parent != -1) { // parent != 1 to avoid creating invalid critical connection, e.g., [-1, 0] + criticalConns.add(Arrays.asList(parent, server)); + } + + return Math.min(visitedAt[server], minVisitedAtOfAllNeighbors); + } + + private List> buildGraph(int n, List> connections) { + List> graph = new ArrayList<>(); + for (int i = 0; i < n; i++) { + graph.add(new ArrayList<>()); + } + + for (List conn : connections) { + graph.get(conn.get(0)).add(conn.get(1)); + graph.get(conn.get(1)).add(conn.get(0)); + } + + return graph; + } +} diff --git a/src/main/java/graph/leetcode/EmployeeImportance.java b/src/main/java/graph/leetcode/EmployeeImportance.java new file mode 100644 index 0000000..4b6a638 --- /dev/null +++ b/src/main/java/graph/leetcode/EmployeeImportance.java @@ -0,0 +1,50 @@ +package graph.leetcode; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * https://leetcode.com/problems/employee-importance/ + */ +public class EmployeeImportance { + + public int getImportance(List employees, int id) { + + Deque stack = new ArrayDeque<>(); + int result = 0; + Set set = new HashSet<>(); + + Map cache = new HashMap<>(); + for (Employee e : employees) { + cache.put(e.id, e); + } + + stack.push(cache.get(id)); + + while (!stack.isEmpty()) { + + Employee emp = stack.pop(); + if (set.contains(emp.id)) continue; + set.add(emp.id); + + result += emp.importance; + + for (Integer e : emp.subordinates) { + stack.push(cache.get(e)); + } + } + + return result; + } + + static class Employee { + public int id; + public int importance; + public List subordinates; + } +} diff --git a/src/main/java/graph/leetcode/EquationEquality.java b/src/main/java/graph/leetcode/EquationEquality.java new file mode 100644 index 0000000..6893f27 --- /dev/null +++ b/src/main/java/graph/leetcode/EquationEquality.java @@ -0,0 +1,93 @@ +package graph.leetcode; + +/** + * tricky UF + * + * https://leetcode.com/problems/satisfiability-of-equality-equations/ + *

+ * Intuition: + * We have 26 nodes in the graph. + * All "==" equations actually represent the connection in the graph. + * The connected nodes should be in the same color/union/set. + *

+ * Then we check all inequations. + * Two inequal nodes should be in the different color/union/set. + *

+ * Explanation + * We can solve this problem by DFS or Union Find. + *

+ * Firt pass all "==" equations. + * Union equal letters together + * Now we know which letters must equal to the others. + *

+ * Second pass all "!=" inequations, + * Check if there are any contradict happens. + */ +public class EquationEquality { + + public boolean equationsPossible(String[] equations) { + + UnionFind uf = new UnionFind(26); + + for (String equation : equations) { + if (equation.charAt(1) == '=' && equation.charAt(2) == '=') { + uf.union(equation.charAt(0) - 'a', equation.charAt(3) - 'a'); + } + } + + for (String equation : equations) { + if (equation.charAt(1) == '!') { + if (uf.isSameComponent(equation.charAt(0) - 'a', equation.charAt(3) - 'a')) { + return false; + } + } + } + + return true; + } + + + static class UnionFind { + int[] parent; + int[] rank; + + public UnionFind(int n) { + this.parent = new int[n]; + this.rank = new int[n]; + + for (int i = 0; i < n; i++) { + this.parent[i] = i; + } + } + + public boolean union(int x, int y) { + + int parentX = find(x); + int parentY = find(y); + + if (parentX == parentY) return false; + + if (rank[parentX] > rank[parentY]) { + parent[parentY] = parentX; + } else if (rank[parentY] > rank[parentX]) { + parent[parentX] = parentY; + } else { + parent[parentY] = parentX; + rank[parentX]++; + } + + return true; + } + + public int find(int x) { + if (parent[x] == x) return x; + + parent[x] = find(parent[x]); + return parent[x]; + } + + public boolean isSameComponent(int p, int q) { + return find(p) == find(q); + } + } +} \ No newline at end of file diff --git a/src/main/java/graph/leetcode/FindAllSafeStates.java b/src/main/java/graph/leetcode/FindAllSafeStates.java new file mode 100644 index 0000000..3ba13f7 --- /dev/null +++ b/src/main/java/graph/leetcode/FindAllSafeStates.java @@ -0,0 +1,43 @@ +package graph.leetcode; + +import java.util.ArrayList; +import java.util.List; + +/** + * https://leetcode.com/problems/find-eventual-safe-states + * + * similar to Topological sort, asks to remove cycles and return non cyclic paths + */ +public class FindAllSafeStates { + + enum State { + VISITED, + VISITING + } + + public List eventualSafeNodes(int[][] graph) { + List safeNodes = new ArrayList<>(graph.length); + State[] states = new State[graph.length]; + for (int node = 0; node < graph.length; node++) { + if (isSafe(graph, node, states)) { + safeNodes.add(node); + } + } + return safeNodes; + } + + private boolean isSafe(int[][] graph, int node, State[] states) { + if (states[node] != null) { + return states[node] == State.VISITED; + } + + states[node] = State.VISITING; + + for (int next : graph[node]) { + if (!isSafe(graph, next, states)) return false; + } + + states[node] = State.VISITED; + return true; + } +} diff --git a/src/main/java/graph/leetcode/FindJudge.java b/src/main/java/graph/leetcode/FindJudge.java new file mode 100644 index 0000000..5017ee9 --- /dev/null +++ b/src/main/java/graph/leetcode/FindJudge.java @@ -0,0 +1,30 @@ +package graph.leetcode; + +/** + * In a town, there are N people labelled from 1 to N. There is a rumor that one of these people is secretly the town judge. + * + * If the town judge exists, then: + * + * The town judge trusts nobody. + * Everybody (except for the town judge) trusts the town judge. + * There is exactly one person that satisfies properties 1 and 2. + * You are given trust, an array of pairs trust[i] = [a, b] representing that the person labelled a trusts the person labelled b. + */ +public class FindJudge { + public int findJudge(int N, int[][] trust) { + if(trust.length==0 && N==1) return 1; + int[] inEdge= new int[N+1]; + int[] outEdge= new int[N+1]; + + for(int[] val: trust){ + outEdge[val[0]]++; + inEdge[val[1]]++; + } + + for(int i=1;i<=N;i++){ + if(outEdge[i]==0 && inEdge[i]==N-1) return i; + } + + return -1; + } +} diff --git a/src/main/java/graph/leetcode/FindTheCity.java b/src/main/java/graph/leetcode/FindTheCity.java new file mode 100644 index 0000000..d5244d7 --- /dev/null +++ b/src/main/java/graph/leetcode/FindTheCity.java @@ -0,0 +1,126 @@ +package graph.leetcode; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.PriorityQueue; + +public class FindTheCity { + + /** + * This problem is path finding with positive weighted edges, + * so Dijkstras immediately pops into mind. Dijkstras is a smallest path finding algorithm that works on graphs with positive weights. + * Dijkstras is a SSSP (Single Source Shortest Path) algorithm, + * so that means we will need to perform Dijkstras on every single vertex and treat it as the start because every city is a potential answer. + * + * + Build the graph using the edges input + Iterate over all the vertices and perform Dijkstras on that vertex as the start node + Dijkstra will tell you how many cities are <= distanceThreshold + Keep a count of the smallest count + + */ + public int findTheCity(int n, int[][] edges, int distanceThreshold) { + Map> g = buildGraph(n ,edges); + int result = 0, reachableCities = n; + for(int city = 0; city < n; city++){ + int[] costs = dijsktra(city, g, n); + + int count = 0; + for(int cost : costs){ + if(cost <= distanceThreshold){ + count++; + } + } + if(count <= reachableCities){ + result = city; + reachableCities = count; + } + } + return result; + } + int[] dijsktra(int sourceCity, Map> g, int n){ + int[] costs = new int[n]; + Arrays.fill(costs, 10001); + + PriorityQueue queue = new PriorityQueue<>(Comparator.comparingInt(a->a[0])); + costs[sourceCity] = 0; + queue.add(new int[]{0,sourceCity}); + while (!queue.isEmpty()){ + int[] temp = queue.poll(); + int currentCity = temp[1]; + int currTime = temp[0]; + + if(costs[currentCity] neighbours = g.get(currentCity); + for(int[] c: neighbours){ + int toCity = c[0], distance = c[1]; + if(costs[toCity] > currTime + distance){ + costs[toCity] = currTime + distance; + queue.add(new int[]{costs[toCity],toCity}); + } + } + } + return costs; + } + + Map> buildGraph(int totalCities, int[][] edges){ + Map> g = new HashMap<>(); + + for(int city = 0; city < totalCities; city++){ + g.put(city, new ArrayList<>()); + } + + for(int[] e : edges){ + int cityA = e[0], cityB = e[1], distance = e[2]; + g.get(cityA).add(new int[]{cityB, distance}); + g.get(cityB).add(new int[]{cityA, distance}); + } + return g; + } + + public int findTheCityFloydWarshal(int n, int[][] edges, int distanceThreshold) { + + int[][] dis = new int[n][n]; + int res = 0, smallest = n; + + for (int[] row : dis) + Arrays.fill(row, 10001); + + for (int[] e : edges){ + int cityA = e[0]; + int cityB = e[1]; + int cost = e[2]; + + dis[cityA][cityB] = cost; + dis[cityB][cityA] = cost; + } + + + for (int i = 0; i < n; ++i) + dis[i][i] = 0; + + + for (int k = 0; k < n; ++k) + for (int i = 0; i < n; ++i) + for (int j = 0; j < n; ++j) + dis[i][j] = Math.min(dis[i][j], dis[i][k] + dis[k][j]); + + + for (int i = 0; i < n; i++) { + int count = 0; + for (int j = 0; j < n; ++j) + if (dis[i][j] <= distanceThreshold) + ++count; + if (count <= smallest) { + res = i; + smallest = count; + } + } + return res; + } + +} diff --git a/src/main/java/graph/leetcode/FriendCircles.java b/src/main/java/graph/leetcode/FriendCircles.java new file mode 100644 index 0000000..2d6d3ed --- /dev/null +++ b/src/main/java/graph/leetcode/FriendCircles.java @@ -0,0 +1,58 @@ +package graph.leetcode; + +// similar to number of Islands +public class FriendCircles { + public int findCircleNum(int[][] M) { + UnionFind uf = new UnionFind(M.length); + for (int i = 0; i < M.length; i++) { + for (int j = 0; j < M[0].length; j++) { + if (M[i][j] == 1) { + uf.union(i, j); + } + } + } + return uf.getCount(); + } + + static class UnionFind { + int[] parent; + int[] rank; + int count; + + public UnionFind(int n) { + parent = new int[n]; + rank = new int[n]; + count = n; + for (int i = 0; i < n; i++) { + parent[i] = i; + } + } + + public int find(int n) { + if (parent[n] == n) return n; + parent[n] = find(parent[n]); + return parent[n]; + } + + public void union(int x, int y) { + int rootX = find(x); + int rootY = find(y); + + if (rootX == rootY) return; + + if (rank[rootX] > rank[rootY]) { + parent[rootY] = rootX; + } else if (rank[rootY] > rank[rootX]) { + parent[rootX] = rootY; + } else { + parent[rootY] = rootX; + rank[rootX]++; + } + count--; + } + + public int getCount() { + return count; + } + } +} diff --git a/src/main/java/graph/leetcode/GraphBiPartite.java b/src/main/java/graph/leetcode/GraphBiPartite.java new file mode 100644 index 0000000..790cb7f --- /dev/null +++ b/src/main/java/graph/leetcode/GraphBiPartite.java @@ -0,0 +1,32 @@ +package graph.leetcode; + +/** + * https://leetcode.com/problems/is-graph-bipartite/ + */ +public class GraphBiPartite { + public boolean isBipartite(int[][] graph) { + Integer[] color = new Integer[graph.length]; + + for (int i = 0; i < graph.length; i++) { //This graph might be a disconnected graph. So check each unvisited node. + if (color[i] == null && !dfs(graph, color, 1, i)) { + return false; + } + } + + return true; + } + + public boolean dfs(int[][] graph, Integer[] colors, int curColor, int node) { + colors[node] = curColor; + + for (int neighbor : graph[node]) { + if (colors[neighbor] == null ){ + if(!dfs(graph, colors, -curColor, neighbor)) // If this node hasn't been colored, Color it with a different color + return false; + } else if (colors[neighbor] == curColor) { + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/src/main/java/graph/leetcode/GraphBiPartitePossiblity.java b/src/main/java/graph/leetcode/GraphBiPartitePossiblity.java new file mode 100644 index 0000000..f688c85 --- /dev/null +++ b/src/main/java/graph/leetcode/GraphBiPartitePossiblity.java @@ -0,0 +1,40 @@ +package graph.leetcode; + +import java.util.ArrayList; +import java.util.List; + +/** + * https://leetcode.com/problems/possible-bipartition/submissions/ + * https://www.youtube.com/watch?v=hWFqtlbnQV8&ab_channel=KnowledgeCenter + */ +public class GraphBiPartitePossiblity { + public boolean possibleBipartition(int N, int[][] dislikes) { + List[] graph = new List[N + 1]; + for (int i = 1; i <= N; ++i) graph[i] = new ArrayList<>(); + for (int[] dislike : dislikes) { + graph[dislike[0]].add(dislike[1]); + graph[dislike[1]].add(dislike[0]); + } + + Integer[] colors = new Integer[N + 1]; + for (int i = 1; i <= N; i++) { + // If the connected component that node i belongs to hasn't been colored yet then try coloring it. + if (colors[i] == null && !dfs(graph, colors, i, 1)) return false; + } + return true; + } + + private boolean dfs(List[] graph, Integer[] colors, int currNode, int currColor) { + colors[currNode] = currColor; + + // Color all uncolored adjacent nodes. + for (Integer adjacentNode : graph[currNode]) { + if (colors[adjacentNode] == null) { + if (!dfs(graph, colors, adjacentNode, currColor * -1)) return false; + } else if (colors[adjacentNode] == currColor) { + return false; + } + } + return true; + } +} diff --git a/src/main/java/graph/leetcode/GraphValidTree.java b/src/main/java/graph/leetcode/GraphValidTree.java new file mode 100644 index 0000000..a18d4f1 --- /dev/null +++ b/src/main/java/graph/leetcode/GraphValidTree.java @@ -0,0 +1,60 @@ +package graph.leetcode; + +public class GraphValidTree { + + public boolean validTree(int n, int[][] edges) { + UnionFind uf = new UnionFind(n); + int M = edges.length; + for (int i = 0; i < M; i++) { + int p = edges[i][0], q = edges[i][1]; + if (uf.find(p) == uf.find(q)) { + return false; + } + uf.union(p, q); + } + + return uf.count == 1; + } + + class UnionFind { + int[] parent; + int[] rank; + int count; + + public UnionFind(int n) { + parent = new int[n]; + rank = new int[n]; + count = n; + for (int i = 0; i < n; i++) { + parent[i] = i; + } + } + + public int find(int n) { + if (parent[n] == n) return n; + parent[n] = find(parent[n]); + return parent[n]; + } + + public void union(int x, int y) { + int rootX = find(x); + int rootY = find(y); + + if (rootX == rootY) return; + + if (rank[rootX] > rank[rootY]) { + parent[rootY] = rootX; + } else if (rank[rootY] > rank[rootX]) { + parent[rootX] = rootY; + } else { + parent[rootY] = rootX; + rank[rootX]++; + } + count--; + } + + public int getCount() { + return count; + } + } +} diff --git a/src/main/java/graph/leetcode/IslandBFS.java b/src/main/java/graph/leetcode/IslandBFS.java new file mode 100644 index 0000000..74ff277 --- /dev/null +++ b/src/main/java/graph/leetcode/IslandBFS.java @@ -0,0 +1,59 @@ +package graph.leetcode; + +import java.util.ArrayDeque; +import java.util.Queue; + +class IslandBFS { + private class Pair { + int x; + int y; + + Pair(int x, int y) { + this.x = x; + this.y = y; + } + } + + public int numIslands(char[][] grid) { + int result = 0; + //i-1,j i+1,j i,j-1 j,j+1 + int[][] directions = new int[][] { { -1, 0 }, { 1, 0 }, { 0, -1 }, { 0, 1 } }; + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[0].length; j++) { + if (grid[i][j] == '1') { + bfsUtil(grid, i, j, directions); + result++; + } + + } + } + return result; + } + + public void bfsUtil(char[][] grid, int i, int j, int[][] directions) { + grid[i][j] = '0'; + Pair root = new Pair(i, j); + + Queue queue = new ArrayDeque<>(); + queue.offer(root); + while (!queue.isEmpty()) { + Pair temp = queue.poll(); + for (int[] dir : directions) { + + int x = temp.x + dir[0]; + int y = temp.y + dir[1]; + if (isvalid(grid, x, y)) { + grid[x][y] = '0'; + queue.offer(new Pair(x, y)); + } + } + } + } + + public boolean isvalid(char[][] grid, int x, int y) { + if (x < 0 || x >= grid.length || y < 0 || y >= grid[0].length || grid[x][y] == '0') { + return false; + } + return true; + } +} \ No newline at end of file diff --git a/src/main/java/graph/leetcode/MakeIslandLarger.java b/src/main/java/graph/leetcode/MakeIslandLarger.java new file mode 100644 index 0000000..75b55de --- /dev/null +++ b/src/main/java/graph/leetcode/MakeIslandLarger.java @@ -0,0 +1,129 @@ +package graph.leetcode; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * https://leetcode.com/problems/making-a-large-island + * + * You are given an n x n binary matrix grid. You are allowed to change at most one 0 to be 1. + * + * Return the size of the largest island in grid after applying this operation. + */ +public class MakeIslandLarger { + + /** + * Worst time is O(M*N)^2 + * this is same as island problem but here we need to change any one of the index + * we change a water area to grid[i][j] = 1; and once calculated its size + * we revert to grid[i][j] = 0; + * so for every i,j we check all the entries. + * + * @param grid + * @return + */ + public int largestIslandBruteForce(int[][] grid) { + int result = 0; + int m = grid.length; + int n = grid[0].length; + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[0].length; j++) { + if (grid[i][j] == 1) continue; + grid[i][j] = 1; + result = Math.max(result, dfs(i, j, new boolean[m][n], grid)); + grid[i][j] = 0; + } + } + + return result == 0 ? m * n : result; + } + + public int dfs(int i, int j, boolean[][] visited, int[][] grid) { + if (i < 0 || i >= visited.length || j < 0 || j >= visited[0].length || visited[i][j] || grid[i][j] == 0) + return 0; + + visited[i][j] = true; + + return 1 + dfs(i + 1, j, visited, grid) + dfs(i, j + 1, visited, grid) + dfs(i - 1, j, visited, grid) + dfs(i, j - 1, visited, grid); + + } + + /** + * https://www.youtube.com/watch?v=_426VVOB8Vo&ab_channel=MichaelMuinos + * time is O(M*N) + *

+ * For each 1 in the grid, we paint all connected 1 with the next available color (2, 3, and so on). + * We also remember the size of the island we just painted with that color. + *

+ * Then, we analyze all 0 in the grid, + * and sum sizes of connected islands (based on the island color). + * Note that the same island can connect to 0 more than once. + * The example below demonstrates this idea (the answer is highlighted): + * input: + * 0 1 0 1 0 + * 1 1 0 0 1 + * 0 0 1 1 0 + *

+ * transformed group: 2 is one group of island, 3 is one group, 4 is one group and 5 is another + * the length of each group is stored in map + * 0 2 0 3 0 + * 2 2 0 0 4 + * 0 0 5 5 0 + *

+ * we iterate the matrix back, whenever we find 0, we see all the neighbours group value + * for example at index grid[0][2] we have 2 and 3 as neighbours, we add both of the group's length + * to sum and add 1 to it because we are considering grid[0][2] is changed to land now + * sum = 1+ 3(2's length) + 1 (3's length) => 5 + * + * @param grid + * @return + */ + + public int largestIsland(int[][] grid) { + Map map = new HashMap<>(); //Key: color, Val: size of island painted of that color + map.put(0, 0); //We won't paint island 0, hence make its size 0, we will use this value later + int n = grid.length; + int colorIndex = 2; //0 and 1 is already used in grid, hence we start colorIndex from 2 + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == 1) { + int size = paint(grid, i, j, colorIndex); + map.put(colorIndex, size); + colorIndex++; + } + } + } + + //If there is no island 0 from grid, res should be the size of islands of first color + //If there is no island 1 from grid, res should be 0 + int res = map.getOrDefault(2, 0); + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == 0) { + //We use a set to avoid repeatedly adding islands with the same color + Set set = new HashSet<>(); + //If current island is at the boundary, we add 0 to the set, whose value is 0 in the map + set.add(i > 0 ? grid[i - 1][j] : 0); + set.add(i < n - 1 ? grid[i + 1][j] : 0); + set.add(j > 0 ? grid[i][j - 1] : 0); + set.add(j < n - 1 ? grid[i][j + 1] : 0); + + int newSize = 1; //We need to count current island as well, hence we init newSize with 1 + for (int color : set) newSize += map.get(color); + res = Math.max(res, newSize); + } + } + } + return res; + } + + //Helper method to paint current island and all its connected neighbors + //Return the size of all painted islands at the end + private int paint(int[][] grid, int i, int j, int color) { + if (i < 0 || j < 0 || i >= grid.length || j >= grid[0].length || grid[i][j] != 1) return 0; + grid[i][j] = color; + return 1 + paint(grid, i + 1, j, color) + paint(grid, i - 1, j, color) + paint(grid, i, j + 1, color) + paint(grid, i, j - 1, color); + } +} diff --git a/src/main/java/graph/leetcode/MatrixStoneRemoval.java b/src/main/java/graph/leetcode/MatrixStoneRemoval.java new file mode 100644 index 0000000..ab436de --- /dev/null +++ b/src/main/java/graph/leetcode/MatrixStoneRemoval.java @@ -0,0 +1,71 @@ +package graph.leetcode; + +/** + * https://leetcode.com/problems/most-stones-removed-with-same-row-or-column + */ +public class MatrixStoneRemoval { + + public int removeStones(int[][] stones) { + + int n = stones.length; + UnionFind uf = new UnionFind(n); + for (int i = 0; i < stones.length; i++) { + for (int j = i + 1; j < stones.length; j++) { + // if any two points are on the same column or row, they are connected as a + // edge. + // find connected component, and remove all but one. + // count the number of disjoint components. + if (stones[i][0] == stones[j][0] || stones[i][1] == stones[j][1]) { + uf.union(i, j); + } + } + } + + /** + * For each island, there will be only 1 stone left after removing the stones on the same island. + * So the left stone after removing all stones is equal to the number of islands, + * and the maximum removed stone number (Note: maximum) will be equal to #stone - #islands. + * + * Why it is the maximum? Considering an island with connecting stone a-b-c. + * If you remove a and c, the maximum count you can remove is 2. However, if you remove 2 first, the remove count will be only 1. + */ + return n - uf.components; + } + + static class UnionFind { + int[] parent; + int[] rank; + int components = 0; + + public UnionFind(int size) { + parent = new int[size]; + for (int i = 0; i < size; i++) { + parent[i] = i; + } + rank = new int[size]; + this.components = size; + } + + public int find(int x) { + if (parent[x] != x) parent[x] = find(parent[x]); + return parent[x]; + } + + public boolean union(int x, int y) { + int xRank = find(x), yRank = find(y); + if (xRank == yRank) { + return false; + } else if (rank[xRank] < rank[yRank]) { + parent[xRank] = yRank; + } else if (rank[xRank] > rank[yRank]) { + parent[yRank] = xRank; + } else { + parent[yRank] = xRank; + rank[xRank]++; + } + components--; + return true; + + } + } +} diff --git a/src/main/java/graph/leetcode/MaxDistanceFromWater.java b/src/main/java/graph/leetcode/MaxDistanceFromWater.java new file mode 100644 index 0000000..81954df --- /dev/null +++ b/src/main/java/graph/leetcode/MaxDistanceFromWater.java @@ -0,0 +1,39 @@ +package graph.leetcode; + +import java.util.LinkedList; +import java.util.Queue; + +/** + * https://leetcode.com/problems/as-far-from-land-as-possible + */ +public class MaxDistanceFromWater { + + public int maxDistance(int[][] grid) { + int m = grid.length, n = grid[0].length; + Queue queue = new LinkedList<>(); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == 1) queue.offer(new int[]{i, j}); + } + } + if (queue.isEmpty() || queue.size() == m * n) return -1; + int[][] dirs = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + int max = 0; + while (!queue.isEmpty()) { + int size = queue.size(); + for (int i = 0; i < size; i++) { + int[] point = queue.poll(); + for (int[] dir : dirs) { + int x = point[0] + dir[0]; + int y = point[1] + dir[1]; + if (x >= 0 && y >= 0 && x < m && y < n && grid[x][y] == 0) { + grid[x][y] = 1;// convert to mark as visited + queue.offer(new int[]{x, y}); + } + } + } + max++; + } + return max - 1; + } +} diff --git a/src/main/java/graph/leetcode/MaxProbabilityPath.java b/src/main/java/graph/leetcode/MaxProbabilityPath.java new file mode 100644 index 0000000..10976b8 --- /dev/null +++ b/src/main/java/graph/leetcode/MaxProbabilityPath.java @@ -0,0 +1,46 @@ +package graph.leetcode; + + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.PriorityQueue; + +/** + * https://leetcode.com/problems/path-with-maximum-probability + */ +public class MaxProbabilityPath { + public double maxProbability(int n, int[][] edges, double[] succProb, int start, int end) { + // Create the weighted undirected graph (adjacency list). + Map>> graph = new HashMap<>(); // It's better to create a separate class instead of using Pair<> but this is leetcode. + for (int i = 0; i < n; ++i) graph.put(i, new ArrayList<>()); + for (int i = 0; i < edges.length; ++i) { + graph.get(edges[i][0]).add(new Pair<>(edges[i][1], succProb[i])); + graph.get(edges[i][1]).add(new Pair<>(edges[i][0], succProb[i])); + } + // Dijkstra to find max probability path from start to end node. + Double[] costs = new Double[n]; + // Order from greatest to lowest probability. + PriorityQueue> queue = new PriorityQueue<>((a, b) -> Double.compare(b.getValue(), a.getValue())); + queue.add(new Pair<>(start, 1.0)); // Initially we are at the start node with a probability of 1. + while (!queue.isEmpty()) { + Pair temp = queue.remove(); + int currNode = temp.getKey(); + double currProb = temp.getValue(); + + if (costs[currNode] != null) continue; + costs[currNode] = currProb; + if (currNode == end) break; + + for (Pair adjacentNode : graph.get(currNode)) { + int adjNode = adjacentNode.getKey(); + double adjProb = adjacentNode.getValue(); + if (costs[adjNode] == null) { + queue.add(new Pair<>(adjNode, currProb * adjProb)); + } + } + } + return costs[end] == null ? 0 : costs[end]; + } +} diff --git a/src/main/java/graph/leetcode/MinCostConnectAllPipes.java b/src/main/java/graph/leetcode/MinCostConnectAllPipes.java new file mode 100644 index 0000000..1c3e78c --- /dev/null +++ b/src/main/java/graph/leetcode/MinCostConnectAllPipes.java @@ -0,0 +1,80 @@ +package graph.leetcode; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +public class MinCostConnectAllPipes { + + /** + * I take it this way: + * We cannot build any well. + * There is one and only one hidding well in my house (house 0). + * The cost to lay pipe between house[i] and my house is wells[i]. + * In order to supply water to the whole village, + * we need to make the village a connected graph. + */ + public int minCostToSupplyWater(int n, int[] wells, int[][] pipes) { + UnionFind uf = new UnionFind(n + 1); + + List edges = new ArrayList<>(); + // build an imaginary edge between node 0 to all other houses so that + // even the wells are converted into edges. Now it's a proper MST problem + for (int i = 0; i < n; i++) { + edges.add(new int[]{0, i + 1, wells[i]}); + } + + Collections.addAll(edges, pipes); + + /// Sort edges in increasing order of edge value + edges.sort(Comparator.comparingInt(a -> a[2])); + + int res = 0; + for (int[] edge : edges) { + int x = edge[0], y = edge[1]; + if (uf.find(x) == uf.find(y)) { + continue; + } + uf.union(x, y); + res += edge[2]; + } + + return res; + } + + static class UnionFind { + int[] parent; + int[] rank; + + UnionFind(int n) { + parent = new int[n]; + rank = new int[n]; + for (int i = 0; i < n; i++) parent[i] = i; + } + + public int find(int n) { + if (parent[n] == n) return n; + parent[n] = find(parent[n]); + return parent[n]; + } + + public boolean union(int x, int y) { + int rootX = find(x); + int rootY = find(y); + /// same root - no need to merge (since both are part of same tree) + if (rootX == rootY) return false; + + /// merge if they have different root + if (parent[rootX] > parent[rootY]) { + parent[rootY] = rootX; + } else if (parent[rootX] < parent[rootY]) { + parent[rootX] = rootY; + } else { + parent[rootY] = rootX; + rank[rootX]++; + } + return true; + } + } +} \ No newline at end of file diff --git a/src/main/java/graph/leetcode/MinCostConnectCities.java b/src/main/java/graph/leetcode/MinCostConnectCities.java new file mode 100644 index 0000000..79045e6 --- /dev/null +++ b/src/main/java/graph/leetcode/MinCostConnectCities.java @@ -0,0 +1,74 @@ +package graph.leetcode; + +import java.util.Arrays; +import java.util.Comparator; + +public class MinCostConnectCities { + public int minimumCost(int n, int[][] connections) { + // Check edge case, we need at least n - 1 connections to + // connect all cities + if (connections.length < n - 1) { + return -1; + } + // We need to first sort by the cost to ensure every time we join + // we are picking the minimum cost (Greedy Approach) + Arrays.sort(connections, Comparator.comparingInt(a -> a[2])); + + int result = 0; + + UnionFind uf = new UnionFind(n); + + // Since the connections are sorted with cost from min to max + // Every single time if we found we can union one connection, + // we add that optimal cost to our totalCost + // Whenever there is only one component left, a.k.a all cities are connected. + // then we are done, just return the totalCost. + for (int[] connection : connections) { + // Remember to subtract 1 since the cities are 1-indexed while our + // Uf class is 0-indexed. + if (uf.union(connection[0]-1, connection[1]-1)) { + result += connection[2]; + } + + if (uf.components == 1) return result; + } + + return -1; + } + + static class UnionFind { + int[] parent; + int[] rank; + int components; + + public UnionFind(int n) { + this.parent = new int[n]; + this.rank = new int[n]; + this.components = n ; + for (int i = 0; i < n; i++) { + this.parent[i] = i; + } + } + + public boolean union(int x, int y) { + int xRank = find(x), yRank = find(y); + if (xRank == yRank) { + return false; + } else if (rank[xRank] < rank[yRank]) { + parent[xRank] = yRank; + } else if (rank[xRank] > rank[yRank]) { + parent[yRank] = xRank; + } else { + parent[yRank] = xRank; + rank[xRank]++; + } + components--; + return true; + } + + public int find(int x) { + if (parent[x] != x) parent[x] = find(parent[x]); + return parent[x]; + } + } +} diff --git a/src/main/java/graph/leetcode/MinCostConnectPoints.java b/src/main/java/graph/leetcode/MinCostConnectPoints.java new file mode 100644 index 0000000..2b969e4 --- /dev/null +++ b/src/main/java/graph/leetcode/MinCostConnectPoints.java @@ -0,0 +1,90 @@ +package graph.leetcode; + + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +/** + * https://leetcode.com/problems/min-cost-to-connect-all-points + */ +public class MinCostConnectPoints { + + public int minCostConnectPoints(int[][] points) { + List edges = new ArrayList<>(); + for (int i = 0; i < points.length; i++) { + for (int j = i + 1; j < points.length; j++) { + edges.add(new Edge(i, j, Math.abs(points[i][0] - points[j][0]) + Math.abs(points[i][1] - points[j][1]))); + } + } + + edges.sort(Comparator.comparingInt(e -> e.dist)); + + DSU dsu = new DSU(points.length); + int minCost = 0; + + for (Edge edge : edges) { + if (dsu.union(edge.p1, edge.p2)) { + minCost += edge.dist; + } + if (dsu.componentSize(edge.p1) == points.length) { + return minCost; + } + } + + return minCost; + } + + static class DSU { + int[] parent; + int[] rank; + + public DSU(int N) { + parent = new int[N]; + for (int i = 0; i < N; i++) { + parent[i] = i; + } + rank = new int[N]; + Arrays.fill(rank, 1); + } + + public int find(int x) { + if (x != parent[x]) parent[x] = find(parent[x]); + return parent[x]; + } + + public boolean union(int x, int y) { + int xp = find(x) ; + int yp = find(y); + + if (xp == yp) return false; + + if (rank[xp] < rank[yp]) { + int t = xp; + xp = yp; + yp = t; + } + + parent[yp] = xp; + rank[xp] += rank[yp]; + return true; + } + + public int componentSize(int x) { + return rank[find(x)]; + } + } + + static class Edge { + int p1; + int p2; + int dist; + + public Edge(int x, int y, int dist) { + this.p1 = x; + this.p2 = y; + this.dist = dist; + } + } +} diff --git a/src/main/java/graph/leetcode/MinCostRepairEdges.java b/src/main/java/graph/leetcode/MinCostRepairEdges.java new file mode 100644 index 0000000..41551ab --- /dev/null +++ b/src/main/java/graph/leetcode/MinCostRepairEdges.java @@ -0,0 +1,79 @@ +package graph.leetcode; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.PriorityQueue; +import java.util.Set; + +/** + * There's an undirected connected graph with n nodes labeled 1..n. + * But some of the edges has been broken disconnecting the graph. Find the minimum cost to repair the edges so that all the nodes are once again accessible from each other. + *

+ * Input: + *

+ * n, an int representing the total number of nodes. + * edges, a list of integer pair representing the nodes connected by an edge. + * edgesToRepair, a list where each element is a triplet representing the pair of nodes + * between which an edge is currently broken and the cost of repearing that edge, + * respectively (e.g. [1, 2, 12] means to repair an edge between nodes 1 and 2, the cost would be 12). + */ +public class MinCostRepairEdges { + public static void main(String[] args) { + int n = 5; + int[][] edges = {{1, 2}, {2, 3}, {3, 4}, {4, 5}, {1, 5}}; + int[][] edgesToRepair = {{1, 2, 12}, {3, 4, 30}, {1, 5, 8}}; + System.out.println(new MinCostRepairEdges().minCostToRepair(n, edges, edgesToRepair)); + } + + + public int minCostToRepair(int n, int[][] edges, int[][] edgesToRepair) { + Set hset = new HashSet<>(); + + PriorityQueue pq = new PriorityQueue<>((a, b) -> a[2] - b[2]); + for (int[] e : edgesToRepair) { + pq.add(e); + hset.add(e[0] + " " + e[1]); + } + + for (int[] e : edges) { + String s = e[0] + " " + e[1]; + if (hset.contains(s)) continue; + int[] ne = new int[]{e[0], e[1], 0}; + pq.add(ne); + } + + int[] parent = new int[n + 1]; + Arrays.fill(parent, -1); + + int sum = 0; + while (!pq.isEmpty()) { + int[] e = pq.poll(); + int u = e[0], v = e[1], w = e[2]; + int up = find(u, parent), vp = find(v, parent); + if (up != vp) { + union(up, vp, parent); + sum += w; + } + } + + System.out.println(sum + " result"); + return sum; + } + + public static void union(int x, int y, int[] parent) { + int xroot = find(x, parent); + int yroot = find(y, parent); + parent[yroot] = xroot; + } + + public static int find(int x, int[] parent) { + if (parent[x] < 0) { + return x; + } + int p = find(parent[x], parent); + parent[x] = p; + return p; + } +} + + diff --git a/src/main/java/graph/leetcode/MinPathHavingMaxDifference.java b/src/main/java/graph/leetcode/MinPathHavingMaxDifference.java new file mode 100644 index 0000000..3308822 --- /dev/null +++ b/src/main/java/graph/leetcode/MinPathHavingMaxDifference.java @@ -0,0 +1,89 @@ +package graph.leetcode; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.PriorityQueue; +import java.util.Set; + +/** + * https://leetcode.com/problems/path-with-minimum-effort/ + *

+ * https://leetcode.com/problems/swim-in-rising-water/ + *

+ * both are almost similar + */ +public class MinPathHavingMaxDifference { + public int minimumEffortPath(int[][] heights) { + + int m = heights.length; + int n = heights[0].length; + + int[][] dist = new int[m][n]; + + for (int[] d : dist) { + Arrays.fill(d, Integer.MAX_VALUE); + } + + PriorityQueue queue = new PriorityQueue<>((a, b) -> Integer.compare(a[2], b[2])); + + queue.offer(new int[]{0, 0, 0}); + dist[0][0] = 0; + Set set = new HashSet<>(); + int[][] dirs = new int[][]{{1, 0}, {0, 1}, {-1, 0}, {0, -1}}; + int result = 0; + while (!queue.isEmpty()) { + + int[] node = queue.poll(); + + int x = node[0]; + int y = node[1]; + int distance = node[2]; + + if (distance > dist[x][y]) continue; + + result = Math.max(result, distance); + if (x == m - 1 && y == n - 1) return result; + + if (!set.add(x + "-" + y)) continue; + for (int[] dir : dirs) { + + int newX = x + dir[0]; + int newY = y + dir[1]; + + if (newX < 0 || newY < 0 || newX >= m || newY >= n) continue; + + int diff = Math.abs(heights[x][y] - heights[newX][newY]); + dist[newX][newY] = diff; + queue.offer(new int[]{newX, newY, diff}); + } + } + + return -1; + } + + public int swimInWater(int[][] grid) { + int n = grid.length; + PriorityQueue pq = new PriorityQueue<>((a, b) -> a[2] - b[2]); + boolean[][] visited = new boolean[n][n]; + int[][] dirs = {{0, -1}, {-1, 0}, {0, 1}, {1, 0}}; + + visited[0][0] = true; + pq.offer(new int[]{0, 0, grid[0][0]}); + while (!pq.isEmpty()) { + int[] info = pq.poll(); + int i = info[0], j = info[1], max = info[2]; + for (int[] dir : dirs) { + int newI = dir[0] + i, newJ = dir[1] + j; + if (newI < 0 || newI >= n || newJ < 0 || newJ >= n) continue; + if (!visited[newI][newJ]) { + visited[newI][newJ] = true; + int newmax = Math.max(max, grid[newI][newJ]); + if (newI == n - 1 && newJ == n - 1) return newmax; + pq.offer(new int[]{newI, newJ, newmax}); + } + } + } + + return 0; + } +} diff --git a/src/main/java/graph/leetcode/MinStepsToCutTrees.java b/src/main/java/graph/leetcode/MinStepsToCutTrees.java new file mode 100644 index 0000000..7d059f6 --- /dev/null +++ b/src/main/java/graph/leetcode/MinStepsToCutTrees.java @@ -0,0 +1,94 @@ +package graph.leetcode; + +import java.util.Comparator; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.PriorityQueue; +import java.util.Queue; +import java.util.Set; + +/** + * https://leetcode.com/problems/cut-off-trees-for-golf-event + */ +public class MinStepsToCutTrees { + public int cutOffTree(List> forest) { + int m = forest.size(); + if (m == 0) return 0; + int n = forest.get(0).size(); + PriorityQueue pq = new PriorityQueue<>(Comparator.comparingInt(a -> a.height)); + + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + int h = forest.get(i).get(j); + if (h > 1) { + pq.offer(new Node(i, j, h)); + } + } + } + + Node source = new Node(0, 0, 0); + Node target = null; + int res = 0; + while (!pq.isEmpty()) { + target = pq.poll(); + int distance = getMinDistance(forest, source, target); + if (distance == -1) return -1; + else res += distance; + source = target; + } + return res; + } + + int getMinDistance(List> forest, Node source, Node target) { + int m = forest.size(), n = forest.get(0).size(); + int[][] dir = new int[][]{{0, 1}, {1, 0}, {-1, 0}, {0, -1}}; + Queue q = new LinkedList<>(); + Set visited = new HashSet<>(); + q.offer(source); + int step = 0; + while (!q.isEmpty()) { + int size = q.size(); + for (int i = 0; i < size; i++) { + Node node = q.poll(); + if (node.equals(target)) { + return step; + } + for (int[] row : dir) { + int x = node.x + row[0]; + int y = node.y + row[1]; + if (x < 0 || y < 0 || x >= m || y >= n || forest.get(x).get(y) == 0) { + continue; + } + Node newNode = new Node(x, y, forest.get(x).get(y)); + if (!visited.contains(newNode)) { + visited.add(newNode); + q.offer(newNode); + } + } + } + step++; + } + return -1; + } + + class Node { + int x, y; + int height; + + Node(int xx, int yy, int hh) { + x = xx; + y = yy; + height = hh; + } + + public int hashCode() { + return x * 31 + y; + } + + public boolean equals(Object obj) { + Node other = (Node) obj; + return (other.x == x) && (other.y == y); + } + } +} diff --git a/src/main/java/graph/leetcode/MinimumHeightTrees.java b/src/main/java/graph/leetcode/MinimumHeightTrees.java new file mode 100644 index 0000000..1f29aba --- /dev/null +++ b/src/main/java/graph/leetcode/MinimumHeightTrees.java @@ -0,0 +1,63 @@ +package graph.leetcode; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +/** + * Basically my code starts from the leaf nodes. + * + * For leaf nodes, their degree = 1, which means each of them is only connected to one node. + * + * In our loop, each time we delete the leaf nodes from our graph(just by putting their degrees to 0), + * and meanwhile we add the new leaf nodes after deleting them to the queue. + * + * So basically in the end, the nodes in the queue would be connected to no other nodes but each other. They should be the answer. + */ +public class MinimumHeightTrees { + + public List findMinHeightTrees(int n, int[][] edges) { + List res = new ArrayList<>(); + if (n <= 0) return res; + //this is needed...since when there is only 1 vertex... the indegree of it will be 0..this case is not included in the following discussion... + if (n == 1) { + res.add(0); + return res; + } + List[] graph = (ArrayList[]) new ArrayList[n]; + for (int i = 0; i < n; i++) { + graph[i] = new ArrayList<>(); + } + + for (int[] e : edges) { + graph[e[0]].add(e[1]); + graph[e[1]].add(e[0]); + } + + int[] indegree = new int[n]; + int cnt = n; + Queue leaves = new LinkedList<>(); + for (int i = 0; i < n; i++) { + indegree[i] = graph[i].size(); + if (indegree[i] == 1) { + leaves.add(i); + } + } + while (cnt > 2) { + int size = leaves.size(); + cnt -= size; + for (int i = 0; i < size; i++) { + int v = leaves.poll(); + for (int w : graph[v]) { + indegree[w]--; + if (indegree[w] == 1) { + leaves.add(w); + } + } + } + } + res.addAll(leaves); + return res; + } +} diff --git a/src/main/java/graph/leetcode/NetworkConnection.java b/src/main/java/graph/leetcode/NetworkConnection.java new file mode 100644 index 0000000..52dc48d --- /dev/null +++ b/src/main/java/graph/leetcode/NetworkConnection.java @@ -0,0 +1,62 @@ +package graph.leetcode; + +/** + * https://leetcode.com/problems/number-of-operations-to-make-network-connected + * + * + Perform union find during the traversal of the connections; deduct the number of components when merging any two components; + If there are less than n - 1 connections, no way to make the network connected; + Otherwise need number of components - 1 operations for the network to be fully connected. + + */ +public class NetworkConnection { + + public int makeConnected(int n, int[][] connections) { + + UnionFind uf = new UnionFind(n); + + for (int[] connection : connections) { + uf.union(connection[0], connection[1]); + } + + return connections.length < n - 1 ? -1 : uf.components - 1; + + } + + static class UnionFind { + int[] parent; + int[] rank; + int components = 0; + + public UnionFind(int size) { + parent = new int[size]; + for (int i = 0; i < size; i++) { + parent[i] = i; + } + rank = new int[size]; + this.components = size; + } + + public int find(int x) { + if (parent[x] != x) parent[x] = find(parent[x]); + return parent[x]; + } + + public boolean union(int x, int y) { + int xRank = find(x), yRank = find(y); + if (xRank == yRank) { + return false; + } else if (rank[xRank] < rank[yRank]) { + parent[xRank] = yRank; + } else if (rank[xRank] > rank[yRank]) { + parent[yRank] = xRank; + } else { + parent[yRank] = xRank; + rank[xRank]++; + } + components--; + return true; + + } + } +} diff --git a/src/main/java/graph/leetcode/NetworkDelayTime.java b/src/main/java/graph/leetcode/NetworkDelayTime.java new file mode 100644 index 0000000..79f5e2e --- /dev/null +++ b/src/main/java/graph/leetcode/NetworkDelayTime.java @@ -0,0 +1,91 @@ +package graph.leetcode; + + +import lombok.Getter; +import lombok.Setter; + +import java.util.*; + +public class NetworkDelayTime { + + public int networkDelayTime(int[][] times, int N, int K) { + Map>> adj = new HashMap<>(); + for (int[] time : times) { + int source = time[0]; + int dest = time[1]; + int travelTime = time[2]; + + adj.putIfAbsent(source, new ArrayList<>()); + adj.get(source).add(new Pair<>(travelTime, dest)); + } + + + int[] signalReceivedAt = new int[N + 1]; + Arrays.fill(signalReceivedAt, Integer.MAX_VALUE); + //distance, node into pq + Queue pq = new PriorityQueue<>((a, b) -> (a[0] - b[0])); + + pq.add(new int[]{0, K}); + + signalReceivedAt[K] = 0; + Set visited = new HashSet<>(); + /** + * + Pop the top node currNode from the priority queue. + Traverse all outgoing edges connected to currNode. + Add the adjacent node neighborNode to the priority queue + only if the current path takes less time than the value at signalReceivedAt[neighborNode]. + Update the time at signalReceivedAt[neighborNode] to current path time. + */ + while (!pq.isEmpty()) { + int[] cur = pq.remove(); + + int currNode = cur[1]; + int currNodeTime = cur[0]; + + if (visited.contains(currNode)) { + continue; + } + if (currNodeTime > signalReceivedAt[currNode]) { + continue; + } + visited.add(currNode); + if (adj.containsKey(currNode)) { + for (Pair next : adj.get(currNode)) { + int time = next.getKey(); + int neighborNode = next.getValue(); + if (signalReceivedAt[neighborNode] > currNodeTime + time) { + signalReceivedAt[neighborNode] = currNodeTime + time; + pq.add(new int[]{signalReceivedAt[neighborNode], neighborNode}); + } + } + } + } + int answer = Integer.MIN_VALUE; + for (int i = 1; i <= N; i++) { + answer = Math.max(answer, signalReceivedAt[i]); + } + + // INT_MAX signifies atleat one node is unreachable + return answer == Integer.MAX_VALUE ? -1 : answer; + } + + public int networkDelayTime_BF(int[][] times, int N, int K) { + double[] disTo = new double[N + 1]; + Arrays.fill(disTo, Double.POSITIVE_INFINITY); + disTo[K - 1] = 0; + for (int i = 1; i < N; i++) { + for (int[] edge : times) { + int u = edge[0] - 1, v = edge[1] - 1, w = edge[2]; + disTo[v] = Math.min(disTo[v], disTo[u] + w); + } + } + double res = Double.MIN_VALUE; + for (double i : disTo) { + res = Math.max(i, res); + } + return res == Double.POSITIVE_INFINITY ? -1 : (int) res; + } + +} + diff --git a/src/main/java/graph/leetcode/NewRoadsMST.java b/src/main/java/graph/leetcode/NewRoadsMST.java new file mode 100644 index 0000000..efe8ba1 --- /dev/null +++ b/src/main/java/graph/leetcode/NewRoadsMST.java @@ -0,0 +1,88 @@ +package graph.leetcode; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.PriorityQueue; +import java.util.Queue; + +/** + * Given an undirected graph with n nodes labeled 1..n. Some of the nodes are already connected. + * The i-th edge connects nodes edges[i][0] and edges[i][1] together. + * Your task is to augment this set of edges with additional edges to connect all the nodes. + * Find the minimum cost to add new edges between the nodes such that all the nodes are accessible from each other. + * Input: n = 6, edges = [[1, 4], [4, 5], [2, 3]], newEdges = [[1, 2, 5], [1, 3, 10], [1, 6, 2], [5, 6, 5]] + * Output: 7 + * Explanation: + * There are 3 connected components [1, 4, 5], [2, 3] and [6]. + * We can connect these components into a single component by connecting node 1 to node 2 and node 1 to node 6 at a minimum cost of 5 + 2 = 7. + */ +public class NewRoadsMST { + public static void main(String[] args) { + int n = 6; + int[][] edges = {{1, 4}, {4, 5}, {2, 3}}; + int[][] newEdges = {{1, 2, 5}, {1, 3, 10}, {1, 6, 2}, {5, 6, 5}}; + System.out.println(minCost(n, edges, newEdges)); + } + + public static int minCost(int n, int[][] edges, int[][] newEdges) { + UF uf = new UF(n + 1); // + 1 because nodes are 1-based + for (int[] edge : edges) { + uf.union(edge[0], edge[1]); + } + + Queue pq = new PriorityQueue<>(newEdges.length, Comparator.comparingInt(e -> e[2])); + pq.addAll(Arrays.asList(newEdges)); + + int totalCost = 0; + // 2 because nodes are 1-based, and we have 1 unused component at index 0 + while (!pq.isEmpty() && uf.count != 2) { + int[] edge = pq.poll(); + if (!uf.connected(edge[0], edge[1])) { + uf.union(edge[0], edge[1]); + totalCost += edge[2]; + } + } + return totalCost; + } + + private static class UF { + private int[] parent; // parent[i] = parent of i + private byte[] rank; // rank[i] = rank of subtree rooted at i (never more than 31) + public int count; // number of connected components + + public UF(int n) { + if (n < 0) throw new IllegalArgumentException(); + parent = new int[n]; + rank = new byte[n]; + for (int i = 0; i < n; i++) { + parent[i] = i; + } + count = n; + } + + public int find(int n) { + if (parent[n] == n) return n; + parent[n] = find(parent[n]); + return parent[n]; + } + + public void union(int p, int q) { + int pr = find(p); + int qr = find(q); + if (pr == qr) return; + if (rank[pr] < rank[qr]) { + parent[pr] = qr; + } else { + parent[qr] = pr; + if (rank[pr] == rank[qr]) rank[pr]++; + } + count--; + } + + public boolean connected(int p, int q) { + return find(p) == find(q); + } + } +} + + diff --git a/src/main/java/graph/leetcode/PacificAtlantic.java b/src/main/java/graph/leetcode/PacificAtlantic.java new file mode 100644 index 0000000..c1ffa43 --- /dev/null +++ b/src/main/java/graph/leetcode/PacificAtlantic.java @@ -0,0 +1,71 @@ +package graph.leetcode; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.List; + +/** + * https://leetcode.com/problems/pacific-atlantic-water-flow/ + * + * tricky flood fill variant + * + * The matrix is the continent with water on it and the boundaries are the oceans. Left and top being Pacific and right and bottom being the Atlantic. + * The water on the continent (in the matrix) wants to flow out in the ocean. (Nature huh.) + * The numbers in the matrix is the height of the water for that point. + * For every point you have to ask the question. Can the water at this point and this height flow out in both the oceans + * under the constraints of flowing through only four(up, down, right, left) directions and flow into channels with same height or less height? + * If yes you return the coordinate of that point. Else you ignore it. + */ +public class PacificAtlantic { + + /** + * Now, if we start from the cells connected to altantic ocean and + * visit all cells having height greater than current cell (water can only flow from a cell to another one with height equal or lower), + * we are able to reach some subset of cells (let's call them A). + * + * Next, we start from the cells connected to pacific ocean and repeat the same process, we find another subset (let's call this one B). + * + * The final answer we get will be the intersection of sets A and B (A ∩ B). + */ + public List> pacificAtlantic(int[][] matrix) { + + List> res = new LinkedList<>(); + if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { + return res; + } + int n = matrix.length, m = matrix[0].length; + //We create a new boolean[][] matrix like above, all the beaches is marked as True (1) in the beginning, which means they can connect to the ocean, + // then we explore from the beach to find out all the paths. The idea is the same for Pacific and Atlantic. + + + boolean[][] pacific = new boolean[n][m]; + boolean[][] atlantic = new boolean[n][m]; + for (int i = 0; i < n; i++) { + dfs(matrix, pacific, Integer.MIN_VALUE, i, 0); + dfs(matrix, atlantic, Integer.MIN_VALUE, i, m - 1); + } + for (int i = 0; i < m; i++) { + dfs(matrix, pacific, Integer.MIN_VALUE, 0, i); + dfs(matrix, atlantic, Integer.MIN_VALUE, n - 1, i); + } + for (int i = 0; i < n; i++) + for (int j = 0; j < m; j++) + if (pacific[i][j] && atlantic[i][j]) + res.add(Arrays.asList(i, j)); + return res; + } + + int[][] dir = new int[][]{{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; + + public void dfs(int[][] matrix, boolean[][] visited, int height, int x, int y) { + int n = matrix.length, m = matrix[0].length; + // since we start from ocean we check increasing height condition matrix[x][y] < height + // the problem asks decreasing height condition so that water can flow + if (x < 0 || x >= n || y < 0 || y >= m || visited[x][y] || matrix[x][y] < height) + return; + visited[x][y] = true; + for (int[] d : dir) { + dfs(matrix, visited, matrix[x][y], x + d[0], y + d[1]); + } + } +} diff --git a/src/main/java/graph/leetcode/Pair.java b/src/main/java/graph/leetcode/Pair.java new file mode 100644 index 0000000..a85ae15 --- /dev/null +++ b/src/main/java/graph/leetcode/Pair.java @@ -0,0 +1,25 @@ +package graph.leetcode; + +import lombok.Getter; +import lombok.Setter; + +/** + * There are N network nodes, labelled 1 to N. + *

+ * Given times, a list of travel times as directed edges times[i] = (u, v, w), where u is the source node, v is the target node, + * and w is the time it takes for a signal to travel from source to target. + *

+ * Now, we send a signal from a certain node K. How long will it take for all nodes to receive the signal? If it is impossible, return -1. + */ + +@Getter +@Setter +public class Pair { + public T key; + public U value; + + public Pair(T key, U value) { + this.key = key; + this.value = value; + } +} diff --git a/src/main/java/graph/leetcode/ReconstructItenary.java b/src/main/java/graph/leetcode/ReconstructItenary.java new file mode 100644 index 0000000..71a0c1c --- /dev/null +++ b/src/main/java/graph/leetcode/ReconstructItenary.java @@ -0,0 +1,62 @@ +package graph.leetcode; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.PriorityQueue; + +/** + * Given a list of airline tickets represented by pairs of departure and arrival airports [from, to], reconstruct the itinerary in order. + * All of the tickets belong to a man who departs from JFK. + * Thus, the itinerary must begin with JFK. + *

+ * Input: [["MUC", "LHR"], ["JFK", "MUC"], ["SFO", "SJC"], ["LHR", "SFO"]] + * Output: ["JFK", "MUC", "LHR", "SFO", "SJC"] + *

+ * Input: [["JFK","SFO"],["JFK","ATL"],["SFO","ATL"],["ATL","JFK"],["ATL","SFO"]] + * Output: ["JFK","ATL","JFK","SFO","ATL","SFO"] + */ +public class ReconstructItenary { + + public List findItinerary(List> tickets) { + if (tickets == null || tickets.isEmpty()) return Collections.emptyList(); + + Map> map = new HashMap<>(); + + for (List ticket : tickets) { + map.putIfAbsent(ticket.get(0), new PriorityQueue<>()); + map.get(ticket.get(0)).offer(ticket.get(1)); + } + List res = new ArrayList<>(); + dfs(map, res, "JFK"); + return res; + } + + /** + * Once we construct the adj list + * JFK : [ATL,SFO] + * ATL : [JFK,SFO] + * SFO : [ATL] + * + * in this case we start at JFK we see the adj list, we take the first val 'ALT' and recurse + * ATL -> JFK + * JFK -> SFO // since we popped ALT we get SFO + * SFO -> ATL + * ATL -> SFO + * + * SFO is empty, so we add to stack + */ + public void dfs(Map> map, List res, String s) { + + PriorityQueue queue = map.get(s); + + while (!queue.isEmpty()) { + String temp = queue.poll(); + dfs(map, res, temp); + } + res.add(0, s); + } +} + diff --git a/src/main/java/graph/leetcode/RedundantConnection.java b/src/main/java/graph/leetcode/RedundantConnection.java new file mode 100644 index 0000000..1f8840c --- /dev/null +++ b/src/main/java/graph/leetcode/RedundantConnection.java @@ -0,0 +1,65 @@ +package graph.leetcode; + +/** + * Input: [[1,2], [1,3], [2,3]] + * Output: [2,3] + * Explanation: The given undirected graph will be like this: + * 1 + * / \ + * 2 - 3 + *

+ * Input: [[1,2], [2,3], [3,4], [1,4], [1,5]] + * Output: [1,4] + * Explanation: The given undirected graph will be like this: + * 5 - 1 - 2 + * | | + * 4 - 3 + */ +public class RedundantConnection { + public int[] findRedundantConnection(int[][] edges) { + + UnionFind uf = new UnionFind(edges.length + 1); + + for (int[] edge : edges) { + if (!uf.union(edge[0], edge[1])) { + return edge; + } + } + return new int[]{-1, -1}; + } + + static class UnionFind { + int[] parent; + int[] rank; + + public UnionFind(int size) { + parent = new int[size]; + for (int i = 0; i < size; i++) { + parent[i] = i; + } + rank = new int[size]; + } + + public int find(int x) { + if (parent[x] != x) parent[x] = find(parent[x]); + return parent[x]; + } + + public boolean union(int x, int y) { + int xRank = find(x), yRank = find(y); + if (xRank == yRank) { + return false; + } else if (rank[xRank] < rank[yRank]) { + parent[xRank] = yRank; + } else if (rank[xRank] > rank[yRank]) { + parent[yRank] = xRank; + } else { + parent[yRank] = xRank; + rank[xRank]++; + } + return true; + } + } +} + + diff --git a/src/main/java/graph/leetcode/RedundantConnectionII.java b/src/main/java/graph/leetcode/RedundantConnectionII.java new file mode 100644 index 0000000..d72d7cf --- /dev/null +++ b/src/main/java/graph/leetcode/RedundantConnectionII.java @@ -0,0 +1,126 @@ +package graph.leetcode; + +/** + * + There are 3 cases for Redundant Connection: + + case 1: two-parent problem such that an error node is with two parents + + 1 + / \ + v v + 2-->3 remove the second parentEdge of the node with two parents + + case 2: cyclic problem such that there is a cycle in the graph + + 1 + / ^ + v \ + 2-->3 remove the edge that forms the cycle + + case 3: two-parent and cyclic problem + + 1 + / ^ + v \ + 2-->3 <- 4 remove [2, 3] (to explain) + + Explanation for case 3: + We do union only if it is not the second parentEdge. Why? + We assume we always remove the second parentEdge. + If there is still cycle remained - that means we made the wrong choice, that is, + we should remove the first parentEdge instead. + + If [[1, 2], [2, 3], [4, 3], [3, 1]], [2, 3] comes before [4, 3], + we remove [4,3], then we union [1, 2], [2, 3], [3, 1], there is still cycle -- so we should remove [2, 3]. + + If [[1, 2], [4, 3], [2, 3], [3, 1]], [4, 3] comes before [2, 3], + we remove [2, 3], then we union [1, 2], [4, 3], [3, 1], there is not cycle -- so we should remove [2, 3]. + + + */ +public class RedundantConnectionII { + public int[] findRedundantDirectedConnection(int[][] edges) { + int numNodes = edges.length, edgeHasMultipleParent = -1, edgeMakesCycle = -1; + int[] parent = new int[numNodes + 1]; + + for (int i = 0; i < numNodes; i++) { + int u = edges[i][0]; + int v = edges[i][1]; + if (parent[v] != 0) { + + /* Assume we removed the second edge. */ + edgeHasMultipleParent = i; + break; + } else + parent[v] = u; + } + + UnionFind uf = new UnionFind(numNodes + 1); + for (int i = 0; i < numNodes; i++) { + if (i == edgeHasMultipleParent) // without this condition it might give a cyclic condition + continue; + int u = edges[i][0]; + int v = edges[i][1]; + if (!uf.union(u, v)) { + edgeMakesCycle = i; + break; + } + } + + /* Handle with the cyclic problem only. */ + if (edgeHasMultipleParent == -1) { + return edges[edgeMakesCycle]; + } + + /* Handle with the cyclic problem when we remove the wrong edge. */ + if (edgeMakesCycle != -1) { + int v = edges[edgeHasMultipleParent][1]; + int u = parent[v]; + return new int[]{u, v}; + } + + /* CHandle with the cyclic problem when we remove the right edge. */ + return edges[edgeHasMultipleParent]; + } + + static class UnionFind { + int[] parent; + int[] rank; + + public UnionFind(int n) { + this.parent = new int[n]; + this.rank = new int[n]; + + for (int i = 0; i < n; i++) { + this.parent[i] = i; + } + } + + public boolean union(int x, int y) { + + int parentX = find(x); + int parentY = find(y); + + if (parentX == parentY) return false; + + if (rank[parentX] > rank[parentY]) { + parent[parentY] = parentX; + } else if (rank[parentY] > rank[parentX]) { + parent[parentX] = parentY; + } else { + parent[parentY] = parentX; + rank[parentX]++; + } + + return true; + } + + public int find(int x) { + if (parent[x] == x) return x; + + parent[x] = find(parent[x]); + return parent[x]; + } + } +} diff --git a/src/main/java/graph/leetcode/RoadsToRome.java b/src/main/java/graph/leetcode/RoadsToRome.java new file mode 100644 index 0000000..2fb00f3 --- /dev/null +++ b/src/main/java/graph/leetcode/RoadsToRome.java @@ -0,0 +1,59 @@ +package graph.leetcode; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; + +/** + * https://leetcode.com/discuss/interview-question/867806/q3-online-microsoft-interview-finding-rome + */ +public class RoadsToRome { + + public static int findRome(int[] A, int[] B) { + Map> adjList = new HashMap<>(); + int indegree[] = new int[A.length + 1]; + Queue queue = new LinkedList<>(); + + int outdegree[] = new int[A.length + 1]; + + for (int i = 0; i < A.length; i++) { + List list = adjList.getOrDefault(A[i], new ArrayList<>()); + list.add(B[i]); + adjList.put(A[i], list); + indegree[B[i]]++; + outdegree[A[i]]++; + } + + for (int i = 0; i < A.length + 1; i++) { + if (indegree[i] == 0) + queue.add(i); + } + int last = -1; + int secondLast = -1; + while (!queue.isEmpty()) { + int city = queue.remove(); + if (last == -1) + last = city; + else { + secondLast = last; + last = city; + } + for (int neighbour : adjList.getOrDefault(city, new ArrayList<>())) { + indegree[neighbour]--; + if (indegree[neighbour] == 0) + queue.add(neighbour); + } + + } + if (outdegree[secondLast] == 0 && outdegree[last] == 0) + return -1; + return last; + } + + public static void main(String[] args) { + findRome(new int[]{1,2,3},new int[]{0,0,0}); + } +} diff --git a/src/main/java/graph/leetcode/SentenceSimilarityII.java b/src/main/java/graph/leetcode/SentenceSimilarityII.java new file mode 100644 index 0000000..e822eac --- /dev/null +++ b/src/main/java/graph/leetcode/SentenceSimilarityII.java @@ -0,0 +1,91 @@ +package graph.leetcode; + +import java.util.HashMap; +import java.util.Map; + +/** + * Given two sentences words1, words2 (each represented as an array of strings), and a list of similar word pairs pairs, determine if two sentences are similar. + * + * For example, words1 = ["great", "acting", "skills"] and words2 = ["fine", "drama", "talent"] are similar, + * if the similar word pairs are pairs = [["great", "good"], ["fine", "good"], ["acting","drama"], ["skills","talent"]]. + * Note that the similarity relation is transitive. For example, if “great” and “good” are similar, and “fine” and “good” are similar, then “great” and “fine” are similar. + */ + +public class SentenceSimilarityII { + + public boolean areSentencesSimilarTwo(String[] words1, String[] words2, String[][] pairs) { + if (words1.length != words2.length) { + return false; + } + + UnionFind uf = new UnionFind(2 * pairs.length); + + Map map = new HashMap<>(); + int id = 0; + + for (String[] pair : pairs) { + for (String word : pair) { + map.putIfAbsent(word, id++); + } + uf.union(map.get(pair[0]), map.get(pair[1])); + } + + + for (int i = 0; i < words1.length; i++) { + String word1 = words1[i]; + String word2 = words2[i]; + + if (word1.equals(word2)) { + continue; + } + + if (!map.containsKey(word1) || !map.containsKey(word2) || uf.find(map.get(word1)) != uf.find(map.get(word2))) { + return false; + } + } + return true; + } + + class UnionFind { + int[] sets; + int[] size; + int count; + + public UnionFind(int n) { + sets = new int[n]; + size = new int[n]; + count = n; + + for (int i = 0; i < n; i++) { + sets[i] = i; + size[i] = 1; + } + } + + public int find(int node) { + while (node != sets[node]) { + node = sets[node]; + } + return node; + } + + public void union(int i, int j) { + int node1 = find(i); + int node2 = find(j); + + if (node1 == node2) { + return; + } + + if (size[node1] < size[node2]) { + sets[node1] = node2; + size[node2] += size[node1]; + } else { + sets[node2] = node1; + size[node1] += size[node2]; + } + --count; + } + } + +} diff --git a/src/main/java/graph/leetcode/ShortestBridge.java b/src/main/java/graph/leetcode/ShortestBridge.java new file mode 100644 index 0000000..429453d --- /dev/null +++ b/src/main/java/graph/leetcode/ShortestBridge.java @@ -0,0 +1,76 @@ +package graph.leetcode; + +import java.util.ArrayDeque; +import java.util.Deque; + +/** + * https://leetcode.com/problems/shortest-bridge/ + */ +public class ShortestBridge { + + // find one island location, add it to queue + // then from queue get the shortest distance to another island + public int shortestBridge(int[][] grid) { + + int m = grid.length; + int n=grid[0].length; + boolean[][] visited = new boolean[m][n]; + Deque queue = new ArrayDeque<>(); + boolean isFound = false; + for(int i=0;i=m || newY>=n || visited[newX][newY]) continue; + + if(grid[newX][newY] == 1) return steps; + visited[newX][newY]=true; + queue.offer(new int[]{newX,newY}); + } + } + steps++; + } + + return -1; + + } + + + public void dfs(int[][] grid, int i, int j, Deque queue, boolean[][] visited){ + + if(i<0 || j<0 || i>=grid.length || j>=grid[0].length || grid[i][j]==0 || visited[i][j]) return; + + queue.offer(new int[]{i,j}); + visited[i][j]=true; + dfs(grid, i,j+1,queue,visited); + dfs(grid, i+1,j,queue,visited); + dfs(grid, i-1,j,queue,visited); + dfs(grid, i,j-1,queue,visited); + } +} diff --git a/src/main/java/graph/leetcode/ShortestPathToGetFood.java b/src/main/java/graph/leetcode/ShortestPathToGetFood.java new file mode 100644 index 0000000..4bbc65c --- /dev/null +++ b/src/main/java/graph/leetcode/ShortestPathToGetFood.java @@ -0,0 +1,66 @@ +package graph.leetcode; + +import java.util.LinkedList; +import java.util.Queue; + +/** + * https://leetcode.com/problems/shortest-path-to-get-food/ + * + * You can save memory by changing the grid - when visiting a point, set it to X, so that "being an obstacle" it will not be visited again. + */ +public class ShortestPathToGetFood { + + int[][] dirs = new int[][]{{1, 0}, {0, 1}, {-1, 0}, {0, -1}}; + + public int getFood(char[][] grid) { + + int m = grid.length; + int n = grid[0].length; + + Queue q = new LinkedList<>(); + q.add(findStart(grid)); + + boolean[][] visited = new boolean[m][n]; + + int step = 0; + while (!q.isEmpty()) { + int len = q.size(); + for (int i = 0; i < len; i++) { + int[] pos = q.poll(); + + int x = pos[0]; + int y = pos[1]; + + if (grid[x][y] == '#') return step; + + for (int[] dir : dirs) { + int newX = x + dir[0]; + int newY = y + dir[1]; + + if (isValid(grid, newX, newY) && !visited[newX][newY]) { + visited[newX][newY] = true; + q.offer(new int[]{newX, newY}); + } + } + } + step++; + } + + return -1; + } + + private int[] findStart(char[][] grid) { + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[0].length; j++) { + if (grid[i][j] == '*') { + return new int[]{i, j}; + } + } + } + throw new RuntimeException(); + } + + private boolean isValid(char[][] grid, int i, int j) { + return i >= 0 && i < grid.length && j >= 0 && j < grid[0].length && grid[i][j] != 'X'; + } +} diff --git a/src/main/java/graph/leetcode/TimeToInformEmployee.java b/src/main/java/graph/leetcode/TimeToInformEmployee.java new file mode 100644 index 0000000..5b769e4 --- /dev/null +++ b/src/main/java/graph/leetcode/TimeToInformEmployee.java @@ -0,0 +1,45 @@ +package graph.leetcode; + +import java.util.ArrayList; +import java.util.Deque; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * https://leetcode.com/problems/time-needed-to-inform-all-employees/submissions/ + */ +public class TimeToInformEmployee { + public int numOfMinutes(int n, int headID, int[] manager, int[] informTime) { + Map> map = new HashMap<>(); // Build the hierarchical tree + Deque queue = new LinkedList<>(); + int result = Integer.MIN_VALUE; + + for (int i = 0; i < manager.length; i++) { + if (!map.containsKey(manager[i])) + map.put(manager[i], new ArrayList<>()); + + map.get(manager[i]).add(i); // Map of manager and its subordinates given by the index i + } + + queue.offer(new int[]{headID, 0}); // head of organizer and corresponding informing time. + + while (!queue.isEmpty()) { + int[] temp = queue.poll(); + int managerAtGivenLevel = temp[0]; + int cumulativeTime = temp[1]; + + result = Math.max(result, cumulativeTime); + + if (map.containsKey(managerAtGivenLevel)) { // if the manager has subordinates + List subordinates = map.get(managerAtGivenLevel); // get the list of subordinates + + for (int i : subordinates) { + queue.offer(new int[]{i, informTime[managerAtGivenLevel] + cumulativeTime}); // add the subordinates as manager and the time taken to inform each of these subordinates + } + } + } + return result; + } +} diff --git a/src/main/java/graph/primsAlgorithm/BinaryMinHeap.java b/src/main/java/graph/primsAlgorithm/BinaryMinHeap.java new file mode 100644 index 0000000..831abaf --- /dev/null +++ b/src/main/java/graph/primsAlgorithm/BinaryMinHeap.java @@ -0,0 +1,145 @@ +package graph.primsAlgorithm; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class BinaryMinHeap { + + private List allNodes = new ArrayList<>(); + private Map nodePosition = new HashMap<>(); + + public class Node { + long weight; + long key; + } + + public boolean containsData(long id) { + return nodePosition.containsKey(id); + } + + public void add(int weight, long l) { + Node node = new Node(); + node.weight = weight; + node.key = l; + allNodes.add(node); + int size = allNodes.size(); + int current = size - 1; + int parentIndex = (current - 1) / 2; + nodePosition.put(node.key, current); + + while (parentIndex >= 0) { + Node parentNode = allNodes.get(parentIndex); + Node currentNode = allNodes.get(current); + if (parentNode.weight > currentNode.weight) { + swap(parentNode, currentNode); + updatePositionMap(parentNode.key, currentNode.key, parentIndex, current); + current = parentIndex; + parentIndex = (parentIndex - 1) / 2; + } else { + break; + } + } + } + + public Long min() { + return allNodes.get(0).key; + } + + public boolean empty() { + return allNodes.size() == 0; + } + + public void decrease(long data, int newWeight) { + Integer position = nodePosition.get(data); + allNodes.get(position).weight = newWeight; + int parent = (position - 1) / 2; + while (parent >= 0) { + if (allNodes.get(parent).weight > allNodes.get(position).weight) { + swap(allNodes.get(parent), allNodes.get(position)); + updatePositionMap(allNodes.get(parent).key, allNodes.get(position).key, parent, position); + position = parent; + parent = (parent - 1) / 2; + } else { + break; + } + } + } + + public Long getWeight(long key) { + Integer position = nodePosition.get(key); + if (position == null) { + return null; + } else { + return allNodes.get(position).weight; + } + } + + public Node extractMinNode() { + int size = allNodes.size() - 1; + Node minNode = new Node(); + minNode.key = allNodes.get(0).key; + minNode.weight = allNodes.get(0).weight; + + long lastNodeWeight = allNodes.get(size).weight; + allNodes.get(0).weight = lastNodeWeight; + allNodes.get(0).key = allNodes.get(size).key; + nodePosition.remove(minNode.key); + nodePosition.remove(allNodes.get(0)); + nodePosition.put(allNodes.get(0).key, 0); + allNodes.remove(size); + + int currentIndex = 0; + size--; + while (true) { + int left = 2 * currentIndex + 1; + int right = 2 * currentIndex + 2; + if (left > size) { + break; + } + if (right > size) { + right = left; + } + int smallerIndex = allNodes.get(left).weight <= allNodes.get(right).weight ? left : right; + if (allNodes.get(currentIndex).weight > allNodes.get(smallerIndex).weight) { + swap(allNodes.get(currentIndex), allNodes.get(smallerIndex)); + updatePositionMap(allNodes.get(currentIndex).key, allNodes.get(smallerIndex).key, currentIndex, + smallerIndex); + currentIndex = smallerIndex; + } else { + break; + } + } + return minNode; + } + + public long extractMin() { + return extractMinNode().key; + } + + private void swap(Node node1, Node node2) { + long weight = node1.weight; + long data = node1.key; + + node1.key = node2.key; + node1.weight = node2.weight; + + node2.key = data; + node2.weight = weight; + } + + private void updatePositionMap(long data1, long data2, int pos1, int pos2) { + nodePosition.remove(data1); + nodePosition.remove(data2); + nodePosition.put(data1, pos1); + nodePosition.put(data2, pos2); + } + + public void printHeap() { + for (Node n : allNodes) { + System.out.print(n.weight + " "); + } + } + +} \ No newline at end of file diff --git a/src/main/java/graph/primsAlgorithm/Graph.java b/src/main/java/graph/primsAlgorithm/Graph.java new file mode 100644 index 0000000..574019b --- /dev/null +++ b/src/main/java/graph/primsAlgorithm/Graph.java @@ -0,0 +1,243 @@ +package graph.primsAlgorithm; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class Graph { + + private List allEdges; + private Map allVertex; + boolean isDirected = false; + + public Graph(boolean isDirected) { + allEdges = new ArrayList(); + allVertex = new HashMap(); + this.isDirected = isDirected; + } + + public void addEdge(long id1, long id2) { + addEdge(id1, id2, 0); + } + + // This works only for directed graph because for undirected graph we can end up + // adding edges two times to allEdges + public void addVertex(Vertex vertex) { + if (allVertex.containsKey(vertex.getId())) { + return; + } + allVertex.put(vertex.getId(), vertex); + for (Edge edge : vertex.getEdges()) { + allEdges.add(edge); + } + } + + public Vertex addSingleVertex(long id) { + if (allVertex.containsKey(id)) { + return allVertex.get(id); + } + Vertex v = new Vertex(id); + allVertex.put(id, v); + return v; + } + + public Vertex getVertex(long id) { + return allVertex.get(id); + } + + public void addEdge(long id1, long id2, int weight) { + Vertex vertex1 = null; + if (allVertex.containsKey(id1)) { + vertex1 = allVertex.get(id1); + } else { + vertex1 = new Vertex(id1); + allVertex.put(id1, vertex1); + } + Vertex vertex2 = null; + if (allVertex.containsKey(id2)) { + vertex2 = allVertex.get(id2); + } else { + vertex2 = new Vertex(id2); + allVertex.put(id2, vertex2); + } + + Edge edge = new Edge(vertex1, vertex2, isDirected, weight); + allEdges.add(edge); + vertex1.addAdjacentVertex(edge, vertex2); + if (!isDirected) { + vertex2.addAdjacentVertex(edge, vertex1); + } + + } + + public List getAllEdges() { + return allEdges; + } + + public Collection getAllVertex() { + return allVertex.values(); + } + + public void setDataForVertex(long id, int data) { + if (allVertex.containsKey(id)) { + Vertex vertex = allVertex.get(id); + vertex.setData(data); + } + } + + @Override + public String toString() { + StringBuffer buffer = new StringBuffer(); + for (Edge edge : getAllEdges()) { + buffer.append(edge.getVertex1() + " " + edge.getVertex2() + " " + edge.getWeight()); + buffer.append("\n"); + } + return buffer.toString(); + } +} + +class Vertex { + long id; + private int data; + private List edges = new ArrayList<>(); + private List adjacentVertex = new ArrayList<>(); + + Vertex(long id) { + this.id = id; + } + + public long getId() { + return id; + } + + public void setData(int data) { + this.data = data; + } + + public int getData() { + return data; + } + + public void addAdjacentVertex(Edge e, Vertex v) { + edges.add(e); + adjacentVertex.add(v); + } + + public String toString() { + return String.valueOf(id); + } + + public List getAdjacentVertexes() { + return adjacentVertex; + } + + public List getEdges() { + return edges; + } + + public int getDegree() { + return edges.size(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (int) (id ^ (id >>> 32)); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Vertex other = (Vertex) obj; + if (id != other.id) + return false; + return true; + } +} + +class Edge { + private boolean isDirected = false; + private Vertex vertex1; + private Vertex vertex2; + private int weight; + + Edge(Vertex vertex1, Vertex vertex2) { + this.vertex1 = vertex1; + this.vertex2 = vertex2; + } + + Edge(Vertex vertex1, Vertex vertex2, boolean isDirected, int weight) { + this.vertex1 = vertex1; + this.vertex2 = vertex2; + this.weight = weight; + this.isDirected = isDirected; + } + + Edge(Vertex vertex1, Vertex vertex2, boolean isDirected) { + this.vertex1 = vertex1; + this.vertex2 = vertex2; + this.isDirected = isDirected; + } + + Vertex getVertex1() { + return vertex1; + } + + Vertex getVertex2() { + return vertex2; + } + + int getWeight() { + return weight; + } + + public boolean isDirected() { + return isDirected; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((vertex1 == null) ? 0 : vertex1.hashCode()); + result = prime * result + ((vertex2 == null) ? 0 : vertex2.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Edge other = (Edge) obj; + if (vertex1 == null) { + if (other.vertex1 != null) + return false; + } else if (!vertex1.equals(other.vertex1)) + return false; + if (vertex2 == null) { + if (other.vertex2 != null) + return false; + } else if (!vertex2.equals(other.vertex2)) + return false; + return true; + } + + @Override + public String toString() { + return "Edge [isDirected=" + isDirected + ", vertex1=" + vertex1 + ", vertex2=" + vertex2 + ", weight=" + weight + + "]"; + } +} diff --git a/src/main/java/graph/primsAlgorithm/PrimMST.java b/src/main/java/graph/primsAlgorithm/PrimMST.java new file mode 100644 index 0000000..e331709 --- /dev/null +++ b/src/main/java/graph/primsAlgorithm/PrimMST.java @@ -0,0 +1,71 @@ +package graph.primsAlgorithm; + +import java.util.*; + +/** + * + * Space complexity - O(E + V) Time complexity - O(ElogV) + * + */ +public class PrimMST { + + public List primMST(Graph graph) { + + BinaryMinHeap minHeap = new BinaryMinHeap(); + Map vertexToEdge = new HashMap<>(); + List result = new ArrayList<>(); + + for (Vertex v : graph.getAllVertex()) { + minHeap.add(Integer.MAX_VALUE, v.getId()); + } + + Vertex startVertex = graph.getAllVertex().iterator().next(); + minHeap.decrease(startVertex.id, 0); + + while (!minHeap.empty()) { + long currentId = minHeap.extractMin(); + Vertex current = graph.getVertex(currentId); + + Edge spanningTreeEdge = vertexToEdge.get(current); + + if (spanningTreeEdge != null) { + result.add(spanningTreeEdge); + } + + for (Edge edge : current.getEdges()) { + Vertex adjacent = getVertexForEdge(current, edge); + + if (minHeap.containsData(adjacent.id) && minHeap.getWeight(adjacent.id) > edge.getWeight()) { + minHeap.decrease(adjacent.id, edge.getWeight()); + vertexToEdge.put(adjacent, edge); + } + } + } + return result; + } + + private Vertex getVertexForEdge(Vertex v, Edge e) { + return e.getVertex1().equals(v) ? e.getVertex2() : e.getVertex1(); + } + + public static void main(String args[]) { + Graph graph = new Graph(false); + + graph.addEdge(1, 2, 3); + graph.addEdge(2, 3, 1); + graph.addEdge(3, 1, 1); + graph.addEdge(1, 4, 1); + graph.addEdge(2, 4, 3); + graph.addEdge(4, 5, 6); + graph.addEdge(5, 6, 2); + graph.addEdge(3, 5, 5); + graph.addEdge(3, 6, 4); + + PrimMST prims = new PrimMST(); + Collection edges = prims.primMST(graph); + for (Edge edge : edges) { + System.out.println(edge); + } + } + +} \ No newline at end of file diff --git a/src/main/java/graph/primsAlgorithm/PrimsMSTArray.java b/src/main/java/graph/primsAlgorithm/PrimsMSTArray.java new file mode 100644 index 0000000..a119093 --- /dev/null +++ b/src/main/java/graph/primsAlgorithm/PrimsMSTArray.java @@ -0,0 +1,54 @@ +package graph.primsAlgorithm; + +class PrimsMSTArray { + + private static final int V = 5; + + int minKey(int key[], Boolean mstSet[]) { + int min = Integer.MAX_VALUE, min_index = -1; + + for (int v = 0; v < V; v++) + if (mstSet[v] == false && key[v] < min) { + min = key[v]; + min_index = v; + } + return min_index; + } + + void printMST(int parent[], int n, int graph[][]) { + System.out.println("Edge \tWeight"); + for (int i = 1; i < V; i++) + System.out.println(parent[i] + " - " + i + "\t" + graph[i][parent[i]]); + } + + void primMST(int graph[][]) { + int parent[] = new int[V]; + int key[] = new int[V]; + Boolean mstSet[] = new Boolean[V]; + for (int i = 0; i < V; i++) { + key[i] = Integer.MAX_VALUE; + mstSet[i] = false; + } + key[0] = 0; + parent[0] = -1; + for (int count = 0; count < V - 1; count++) { + int u = minKey(key, mstSet); + mstSet[u] = true; + for (int v = 0; v < V; v++) { + System.out.println(graph[u][v] + "--" + key[v]); + if (graph[u][v] != 0 && mstSet[v] == false && graph[u][v] < key[v]) { + parent[v] = u; + key[v] = graph[u][v]; + } + } + } + printMST(parent, V, graph); + } + + public static void main(String[] args) { + PrimsMSTArray t = new PrimsMSTArray(); + int graph[][] = new int[][] { { 0, 0, 0, 6, 2 }, { 0, 0, 3, 8, 5 }, { 0, 3, 0, 0, 7 }, { 6, 8, 0, 0, 9 }, + { 2, 5, 7, 9, 0 } }; + t.primMST(graph); + } +} diff --git a/src/main/java/graph/shortestPath/Graph.java b/src/main/java/graph/shortestPath/Graph.java new file mode 100644 index 0000000..78514e9 --- /dev/null +++ b/src/main/java/graph/shortestPath/Graph.java @@ -0,0 +1,53 @@ +package graph.shortestPath; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class Graph { + + List> allEdges = new ArrayList>(); + HashMap> adjacentList = new HashMap>(); + + public void addEdge(int src, int dest, int weight) { + Vertex vertex1 = null; + if (adjacentList.containsKey(src)) { + vertex1 = adjacentList.get(src); + } else { + vertex1 = new Vertex(src); + adjacentList.put(src, vertex1); + } + + Vertex vertex2 = null; + if (adjacentList.containsKey(dest)) { + vertex2 = adjacentList.get(dest); + } else { + vertex2 = new Vertex(src); + adjacentList.put(src, vertex2); + } + + allEdges.add(new Edge(vertex1, vertex2, weight)); + allEdges.add(new Edge(vertex2, vertex1, weight)); + } + + class Edge { + Vertex src; + Vertex dest; + int weight; + + public Edge(Vertex src, Vertex dest, int weight) { + this.src = src; + this.dest = dest; + this.weight = weight; + } + } + + class Vertex { + int id; + + public Vertex(int id) { + this.id = id; + } + } + +} diff --git a/src/main/java/graph/shortestPath/ShortestPath.java b/src/main/java/graph/shortestPath/ShortestPath.java new file mode 100644 index 0000000..99e208a --- /dev/null +++ b/src/main/java/graph/shortestPath/ShortestPath.java @@ -0,0 +1,60 @@ +package graph.shortestPath; + +import java.util.LinkedList; + +public class ShortestPath { + + class Graph { + int vertices; + LinkedList adjacentList[]; + + public Graph(int vertices) { + this.vertices = vertices; + adjacentList = new LinkedList[vertices]; + + for (int i = 0; i < vertices; i++) { + adjacentList[i] = new LinkedList(); + } + } + + void addEdge(int src, int dest) { + adjacentList[src].add(dest); + adjacentList[dest].add(src); + } + } + + public static void main(String[] args) { + ShortestPath sp = new ShortestPath(); + Graph graph = sp.new Graph(8); + graph.addEdge(1, 2); + graph.addEdge(1, 0); + graph.addEdge(0, 3); + graph.addEdge(7, 3); + graph.addEdge(7, 4); + graph.addEdge(4, 5); + graph.addEdge(6, 5); + graph.addEdge(3, 4); + + LinkedList visited = new LinkedList(); + sp.shortestPath(0, 7, graph, visited); + + } + + private void shortestPath(int src, int dest, Graph graph, LinkedList visited) { + + LinkedList linkedList = graph.adjacentList[src]; + if (visited.contains(src) || linkedList.isEmpty() ) { + return; + } + + visited.add(src); + for (Integer val : linkedList) { + if (dest == val) { + System.out.println(val); + return; + } + shortestPath(val, dest, graph,visited); + } + } + +} diff --git a/src/main/java/graph/topologicalsort/Graph.java b/src/main/java/graph/topologicalsort/Graph.java new file mode 100644 index 0000000..30244d6 --- /dev/null +++ b/src/main/java/graph/topologicalsort/Graph.java @@ -0,0 +1,245 @@ +package graph.topologicalsort; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class Graph{ + + private List> allEdges; + private Map> allVertex; + boolean isDirected = false; + + public Graph(boolean isDirected){ + allEdges = new ArrayList>(); + allVertex = new HashMap>(); + this.isDirected = isDirected; + } + + public void addEdge(long id1, long id2){ + addEdge(id1,id2,0); + } + + //This works only for directed graph because for undirected graph we can end up + //adding edges two times to allEdges + public void addVertex(Vertex vertex){ + if(allVertex.containsKey(vertex.getId())){ + return; + } + allVertex.put(vertex.getId(), vertex); + for(Edge edge : vertex.getEdges()){ + allEdges.add(edge); + } + } + + public Vertex addSingleVertex(long id){ + if(allVertex.containsKey(id)){ + return allVertex.get(id); + } + Vertex v = new Vertex(id); + allVertex.put(id, v); + return v; + } + + public Vertex getVertex(long id){ + return allVertex.get(id); + } + + public void addEdge(long id1,long id2, int weight){ + Vertex vertex1 = null; + if(allVertex.containsKey(id1)){ + vertex1 = allVertex.get(id1); + }else{ + vertex1 = new Vertex(id1); + allVertex.put(id1, vertex1); + } + Vertex vertex2 = null; + if(allVertex.containsKey(id2)){ + vertex2 = allVertex.get(id2); + }else{ + vertex2 = new Vertex(id2); + allVertex.put(id2, vertex2); + } + + Edge edge = new Edge(vertex1,vertex2,isDirected,weight); + allEdges.add(edge); + vertex1.addAdjacentVertex(edge, vertex2); + if(!isDirected){ + vertex2.addAdjacentVertex(edge, vertex1); + } + + } + + public List> getAllEdges(){ + return allEdges; + } + + public Collection> getAllVertex(){ + return allVertex.values(); + } + public void setDataForVertex(long id, T data){ + if(allVertex.containsKey(id)){ + Vertex vertex = allVertex.get(id); + vertex.setData(data); + } + } + + @Override + public String toString(){ + StringBuffer buffer = new StringBuffer(); + for(Edge edge : getAllEdges()){ + buffer.append(edge.getVertex1() + " " + edge.getVertex2() + " " + edge.getWeight()); + buffer.append("\n"); + } + return buffer.toString(); + } +} + + +class Vertex { + long id; + private T data; + private List> edges = new ArrayList<>(); + private List> adjacentVertex = new ArrayList<>(); + + Vertex(long id){ + this.id = id; + } + + public long getId(){ + return id; + } + + public void setData(T data){ + this.data = data; + } + + public T getData(){ + return data; + } + + public void addAdjacentVertex(Edge e, Vertex v){ + edges.add(e); + adjacentVertex.add(v); + } + + public String toString(){ + return String.valueOf(id); + } + + public List> getAdjacentVertexes(){ + return adjacentVertex; + } + + public List> getEdges(){ + return edges; + } + + public int getDegree(){ + return edges.size(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + (int) (id ^ (id >>> 32)); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Vertex other = (Vertex) obj; + if (id != other.id) + return false; + return true; + } +} + +class Edge{ + private boolean isDirected = false; + private Vertex vertex1; + private Vertex vertex2; + private int weight; + + Edge(Vertex vertex1, Vertex vertex2){ + this.vertex1 = vertex1; + this.vertex2 = vertex2; + } + + Edge(Vertex vertex1, Vertex vertex2,boolean isDirected,int weight){ + this.vertex1 = vertex1; + this.vertex2 = vertex2; + this.weight = weight; + this.isDirected = isDirected; + } + + Edge(Vertex vertex1, Vertex vertex2,boolean isDirected){ + this.vertex1 = vertex1; + this.vertex2 = vertex2; + this.isDirected = isDirected; + } + + Vertex getVertex1(){ + return vertex1; + } + + Vertex getVertex2(){ + return vertex2; + } + + int getWeight(){ + return weight; + } + + public boolean isDirected(){ + return isDirected; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((vertex1 == null) ? 0 : vertex1.hashCode()); + result = prime * result + ((vertex2 == null) ? 0 : vertex2.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + Edge other = (Edge) obj; + if (vertex1 == null) { + if (other.vertex1 != null) + return false; + } else if (!vertex1.equals(other.vertex1)) + return false; + if (vertex2 == null) { + if (other.vertex2 != null) + return false; + } else if (!vertex2.equals(other.vertex2)) + return false; + return true; + } + + @Override + public String toString() { + return "Edge [isDirected=" + isDirected + ", vertex1=" + vertex1 + + ", vertex2=" + vertex2 + ", weight=" + weight + "]"; + } +} + + \ No newline at end of file diff --git a/src/main/java/graph/topologicalsort/TopologicalSort.java b/src/main/java/graph/topologicalsort/TopologicalSort.java new file mode 100644 index 0000000..6152b2a --- /dev/null +++ b/src/main/java/graph/topologicalsort/TopologicalSort.java @@ -0,0 +1,51 @@ +package graph.topologicalsort; + +import java.util.HashSet; +import java.util.Set; +import java.util.Stack; + +/** + * Space and time complexity is O(n). + */ +public class TopologicalSort { + + public Stack> topologicalSort(Graph graph) { + Stack> stack = new Stack<>(); + Set> visited = new HashSet<>(); + + for (Vertex vertex : graph.getAllVertex()) { + if (!visited.contains(vertex)) { + topSortUtil(stack, visited, vertex); + } + } + return stack; + } + + private void topSortUtil(Stack> stack, Set> visited, Vertex vertex) { + visited.add(vertex); + for (Vertex vert : vertex.getAdjacentVertexes()) { + if (!visited.contains(vert)) { + topSortUtil(stack, visited, vert); + } + } + stack.add(vertex); + } + + public static void main(String args[]) { + Graph graph = new Graph<>(true); + graph.addEdge(1, 3); + graph.addEdge(1, 2); + graph.addEdge(3, 4); + graph.addEdge(5, 6); + graph.addEdge(6, 3); + graph.addEdge(3, 8); + graph.addEdge(8, 11); + graph.addEdge(5, 3); + + TopologicalSort sort = new TopologicalSort<>(); + Stack> result = sort.topologicalSort(graph); + while (!result.isEmpty()) { + System.out.println(result.pop()); + } + } +} \ No newline at end of file diff --git a/src/main/java/graph/topologicalsort/TopologicalSortList.java b/src/main/java/graph/topologicalsort/TopologicalSortList.java new file mode 100644 index 0000000..b210b26 --- /dev/null +++ b/src/main/java/graph/topologicalsort/TopologicalSortList.java @@ -0,0 +1,65 @@ +package graph.topologicalsort; + +import java.util.LinkedList; +import java.util.List; +import java.util.Stack; + +public class TopologicalSortList { + + class Graph { + List adjacentList[]; + int vertices; + + public Graph(int vertices) { + this.vertices = vertices; + adjacentList = new LinkedList[vertices]; + + for (int i = 0; i < vertices; i++) { + adjacentList[i] = new LinkedList(); + } + } + } + + public static void main(String[] args) { + TopologicalSortList ts = new TopologicalSortList(); + Graph graph = ts.new Graph(8); + ts.addEdge(0, 2, graph); + ts.addEdge(1, 2, graph); + ts.addEdge(1, 3, graph); + ts.addEdge(2, 4, graph); + ts.addEdge(4, 5, graph); + ts.addEdge(4, 7, graph); + ts.addEdge(5, 6, graph); + + Stack stack = new Stack(); + Stack visited = new Stack(); + + for (int i = 0; i < graph.vertices; i++) { + if (!visited.contains(i)) + ts.topologicalSort(graph, i, stack, visited); + } + + stack.forEach(System.out::println); + } + + private void topologicalSort(Graph graph, int i, Stack stack, Stack visited) { + if (visited.contains(i)) { + return; + } + visited.push(i); + List list = graph.adjacentList[i]; + if (list.isEmpty()) { + stack.push(i); + } + for (Integer node : list) { + topologicalSort(graph, node, stack, visited); + } + if (!list.isEmpty()) { + stack.push(i); + } + } + + private void addEdge(int i, int j, Graph graph) { + graph.adjacentList[i].add(j); + } +} diff --git a/src/main/java/internals/ConcurrentHashMap.java b/src/main/java/internals/ConcurrentHashMap.java new file mode 100644 index 0000000..ba431c6 --- /dev/null +++ b/src/main/java/internals/ConcurrentHashMap.java @@ -0,0 +1,285 @@ +package internals; + +import java.util.LinkedList; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +public class ConcurrentHashMap { + +// final Segment[] segments; +// // Other varialbes +// +// static final class HashEntry { +// final int hash; +// final K key; +// volatile V value; +// volatile HashEntry next; +// // Constructors + utility methods +// } +// +// static final class Segment extends ReentrantLock implements Serializable { +// // Implementations of methods like replace,clear,put etc. +// // Each such operation is handled by the particular Segment. +// } +// +// public boolean isEmpty() { +// // checks by iterating through all the segments. +// } +// +// public int size() { +// // Iterates through all the segments to get the count/size. +// } +// +// // Performed without acquiring the lock. +// public V get(Object key) { +// // Get segment +// // Get HashEntries for that Segment +// // itarate over the HashEntries to compare the Key. +// } +// +// public boolean containsValue(Object value) { +// // Iterates over all the Segments. +// // To check any Segment, locks it and then gets all HashEntries, and compare each of them. +// // Releases the lock for that segment after it. +// } +// +// public V put(K key, V value) { +// // Gets the Segment, and calls the put method implemented within that segment. +// } +// +// public V remove(Object key) { +// // Gets the Segment, and calls the remove method implemented within that segment. +// } +// +// @Override +// public boolean remove(Object key, Object value) { +// return false; +// } +// +// public boolean replace(K key, V oldValue, V newValue) { +// // Gets the Segment, and calls the replace method implemented within that segment. +// } +// +// public void clear() { +// // Gets the Segment, and calls the clear method implemented within that segment. +// } +// public V get(Object key) { +// Segment s; // manually integrate access methods to reduce overhead +// HashEntry[] tab; +// int h = hash(key.hashCode()); +// long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE; +// if ((s = (Segment)UNSAFE.getObjectVolatile(segments, u)) != null && +// (tab = s.table) != null) { +// for (HashEntry e = (HashEntry) UNSAFE.getObjectVolatile +// (tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE); +// e != null; e = e.next) { +// K k; +// if ((k = e.key) == key || (e.hash == h && key.equals(k))) +// return e.value; +// } +// } +// return null; +// } +// +// +// @Override +// public Set> entrySet() { +// return null; +// } + + + private static final int DEFAULT_CONCURRENCY_LEVEL = 16; + + private static final int INITIAL_CAPACITY = 16; + + private float LOAD_FACTOR = 0.75f; + + private int capacity = INITIAL_CAPACITY; + + private int size; + + private Lock[] locks; + + private MyHashMap myHashMap = new MyHashMap(); + + public ConcurrentHashMap(int concurrencyLevel) { + locks = new Lock[concurrencyLevel]; + for (int i = 0; i < concurrencyLevel; i++) { + locks[i] = new ReentrantLock(); + } + } + private class MyHashMap { + private LinkedList[] lists = new LinkedList[INITIAL_CAPACITY]; + + private class MapEntry { + final Object key; + volatile Object value; + + MapEntry(Object key, Object value) { + this.key = key; + this.value = value; + } + } + + void put(Object key, Object value) { + if (key == null) + throw new IllegalArgumentException("Key Cannot be Null"); + int hash = key.hashCode(); + hash %= lists.length; + if (size >= LOAD_FACTOR * lists.length) { + capacity = lists.length * 2; + LinkedList[] tempLists = new LinkedList[capacity]; + System.arraycopy(lists, 0, tempLists, 0, lists.length); + lists = tempLists; + reHash(); + } + if (lists[hash] == null) { + lists[hash] = new LinkedList<>(); + size++; + } + int i = 0; + for (; i < lists[hash].size(); i++) { + MapEntry mapEntry = (MapEntry) (lists[hash].get(i)); + if (mapEntry != null && mapEntry.key.equals(key)) { + mapEntry.value = value; + break; + } + } + if (i == lists[hash].size()) { + lists[hash].addLast(new MapEntry(key, value)); + } + } + + Object get(Object key) { + int hash = key.hashCode(); + hash %= lists.length; + Object value = null; + if (lists[hash] != null) { + for (int i = 0; i < lists[hash].size(); i++) { + MapEntry mapEntry = (MapEntry) (lists[hash].get(i)); + if (mapEntry != null && mapEntry.key.equals(key)) { + value = mapEntry.value; + break; + } + } + } + return value; + } + + private void reHash() { + for (int i = 0; i < lists.length; i++) { + if (lists[i] != null) { + int hash = ((MapEntry) lists[i].getFirst()).key.hashCode(); + hash %= lists.length; + if (i != hash) { + lists[hash] = lists[i]; + lists[i] = null; + } + } + } + + } + + int size() { + return size; + } + + int capacity() { + return capacity; + } + } + int capacity() { + return capacity; + } + + + public ConcurrentHashMap() { + this(DEFAULT_CONCURRENCY_LEVEL); + } + + public void put(Object key, Object value) { + int hash = key.hashCode(); + hash %= myHashMap.capacity(); + locks[hash].lock(); + myHashMap.put(key, value); + locks[hash].unlock(); + } + + public Object get(Object key) { + return myHashMap.get(key); + } + + public static void main(String[] args) { + ConcurrentHashMap myCCHashMap = new ConcurrentHashMap(); + myCCHashMap.put(1, "Thomas"); + myCCHashMap.put(9, "Mathew"); + myCCHashMap.put(17, "Tissa"); + myCCHashMap.put(9, "Mathew Thomas"); + System.out.println(myCCHashMap.get(1)); + System.out.println(myCCHashMap.get(9)); + System.out.println(myCCHashMap.get(17)); + } +} + +// final class Segment extends ReentrantLock implements Serializable { +// +// transient volatile HashEntry[] table; +// transient int threshold; +// final float loadFactor; +// +// Segment(float loadFactor) { +// this.loadFactor = loadFactor; +// } +// +// final V put(K key, int hash, V value, boolean onlyIfAbsent) { +// // Obtain the lock on the current Segment +// HashEntry node = tryLock() ? null : scanAndLockForPut(key, hash, value); +// try { +// // Do normal Put operation like in hashmap +// } finally { +// // Release the lock after put is finished. +// unlock(); +// } +// } +// +// /** +// * Remove; match on key only if value null, else match both. +// */ +// final V remove(Object key, int hash, Object value) { +// // Obtain the lock on the current Segment +// if (!tryLock()) +// scanAndLock(key, hash); +// +// try { +// // Normal Remove operation as in HashMap. +// } finally { +// // Release the lock once the remove is done. +// unlock(); +// } +// +// } +// +// final boolean replace(K key, int hash, V oldValue, V newValue) { +// // Obtain the lock on the current Segment +// if (!tryLock()) +// scanAndLock(key, hash); +// try { +// // Normal Replace operation as in HashMap. +// } finally { +// // Release the lock once the remove is done. +// unlock(); +// } +// return replaced; +// } +// +// final void clear() { +// // Obtain the lock on the current Segment +// lock(); +// try { +// // Normal clear operation as in HashMap. +// } finally { +// // Release the lock once the clear is done. +// unlock(); +// } +// } +//} \ No newline at end of file diff --git a/src/main/java/internals/HashMap.java b/src/main/java/internals/HashMap.java new file mode 100644 index 0000000..955f09f --- /dev/null +++ b/src/main/java/internals/HashMap.java @@ -0,0 +1,178 @@ +package internals; + +import java.util.Objects; + +class HashMap { + private int size = 0; + private int capacity = 16; + private Entry[] entries = new Entry[capacity]; + private double loadFactor = 0.75; + + private static class Entry { + private final K key; + private V value; + // Java initializes reference fields to null by default. + //private Entry next = null; + private Entry next; + + // No point in 'public' here: + /*public*/ Entry(K key, V value) { + this.key = key; + this.value = value; + } + } + + public HashMap() { + } + + public boolean isEmpty() { + return this.size == 0; + } + + public int getSize() { + return this.size; + } + + public boolean containsKey(K key) { + Entry matchingEntry = getMatchingEntry(key); + + return matchingEntry != null && matchingEntry.key == key; + } + + public boolean containsValue(V value) { + for (Entry entry : this.entries) { + while (entry != null && !matches(value, entry.value)) { + entry = entry.next; + } + + if (entry != null) { + return true; + } + } + + return false; + } + + public V get(K key) { + Entry matchingEntry = getMatchingEntry(key); + + return matchingEntry == null ? null : matchingEntry.value; + } + + public void put(K key, V value) { + if (this.shouldResize()) { + this.resize(); + } + + if (addEntry(new Entry<>(key, value), this.entries)) { + this.size++; + } + + } + + public void remove(K key) { + int index = indexOf(key); + Entry currentEntry = this.entries[index]; + + while (currentEntry != null && currentEntry.next != null && !matches(key, currentEntry.next.key)) { + currentEntry = currentEntry.next; + } + + if (currentEntry != null) { + // this case can only occur if there is only one non-null entry at the index + if (matches(key, currentEntry.key)) { + this.entries[index] = null; + // this case can only occur because the next entry's key matched + } else if (currentEntry.next != null) { + currentEntry.next = currentEntry.next.next; + } + + this.size--; + } + } + + private boolean shouldResize() { + return this.size > Math.ceil((double) this.capacity * this.loadFactor); + } + + private void resize() { + this.capacity = this.size * 2; + + Entry[] newEntries = new Entry[this.capacity]; + for (Entry entry : this.entries) { + if (entry != null) { + this.setEntry(entry, newEntries); + } + } + + this.entries = newEntries; + } + + private void setEntry(Entry entry, Entry[] entries){ + Entry nextEntry = entry.next; + entry.next = null; + + this.addEntry(entry, entries); + + if (nextEntry != null) { + this.setEntry(nextEntry, entries); + } + } + + private boolean addEntry(Entry entry, Entry[] entries) { + int index = indexOf(entry.key); + Entry existingEntry = entries[index]; + + if (existingEntry == null) { + entries[index] = entry; + return true; + } else { + while (!this.matches(entry.key, existingEntry.key) && existingEntry.next != null) { + existingEntry = existingEntry.next; + } + + if (this.matches(entry.key, existingEntry.key)) { + existingEntry.value = entry.value; + return false; + } + + existingEntry.next = entry; + return true; + + } + } + + private Entry getMatchingEntry(K key) { + Entry existingEntry = this.entries[indexOf(key)]; + + while (existingEntry != null && !matches(key, existingEntry.key)) { + existingEntry = existingEntry.next; + } + + return existingEntry; + } + + private int indexOf(K object) { + return object == null ? 0 : hash(object) & (this.capacity - 1); + } + + private boolean matches(Object o1, Object o2) { + return Objects.equals(o1, o2); + } + + /** + * Applies a supplemental hash function to a given hashCode, which + * defends against poor quality hash functions. This is critical + * because HashMap uses power-of-two length hash tables, that + * otherwise encounter collisions for hashCodes that do not differ + * in lower bits. Note: Null keys always map to hash 0, thus index 0. + */ + private static int hash(Object key) { + // This function ensures that hashCodes that differ only by + // constant multiples at each bit position have a bounded + // number of collisions (approximately 8 at default load factor). + int h; + return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16); + } + } + diff --git a/src/main/java/internals/HashMapJava8.java b/src/main/java/internals/HashMapJava8.java new file mode 100644 index 0000000..7b7a119 --- /dev/null +++ b/src/main/java/internals/HashMapJava8.java @@ -0,0 +1,82 @@ +package internals; + +public class HashMapJava8 { + private final Entry[] entries; + private Integer size; + + public HashMapJava8(Entry[] entries, Integer size) { + this.entries = entries; + this.size = size; + } + + public HashMapJava8(int size){ + this.size=size; + this.entries= new Entry[size]; + for(int i=0;i put(K key, V value){ + int hash= getHash(key); + Entry oldEntry= entries[hash]; + Entry newEntry= new Entry(key,value); + newEntry.next=oldEntry; + entries[hash]= newEntry; + size++; + return new HashMapJava8<>(entries,size); + } + +} + +class Entry{ + Object key; + Object value; + Entry next; + public Entry(){ + + } + + public Entry(Object key, Object value) { + this.key = key; + this.value = value; + this.next=null; + } + + public Object getKey() { + return key; + } + + public void setKey(Object key) { + this.key = key; + } + + public Object getValue() { + return value; + } + + public void setValue(Object value) { + this.value = value; + } + + public Entry getNext() { + return next; + } + + public void setNext(Entry next) { + this.next = next; + } + + @Override + public String toString() { + return "Entry{" + + "key=" + key + + ", value=" + value + + ", next=" + next + + '}'; + } +} diff --git a/src/main/java/internals/LinkedHashMap.java b/src/main/java/internals/LinkedHashMap.java new file mode 100644 index 0000000..9c119b6 --- /dev/null +++ b/src/main/java/internals/LinkedHashMap.java @@ -0,0 +1,170 @@ +package internals; + +public class LinkedHashMap { + private Entry[] buckets; + private int capacity=4; + private Entry head; + private Entrytail; + static class Entry + { + K key; + V value; + Entrynext; + Entrybefore; + Entryafter; + public Entry(K key ,V value,Entry next) + { + this.key=key; + this.value=value; + this.next=next; + } + } + public LinkedHashMap() + { + buckets=new Entry[capacity]; + } + public void put(K key,V value) + { + if(key==null)//not allow null key + { + return; + } + boolean replace=false; + int hash=hash(key); + Entry newEntry = new Entry(key, value, null); + //insert in bucket + // maintainOrderAfterInsert(newEntry); + Entrycurr=buckets[hash]; + + if(curr==null) + { + buckets[hash]=newEntry; + } + else + { + Entry prev=null; + while(curr!=null) + { + if(curr.key.equals(key)) + { + replace=true; + curr.value=value; + break; + } + prev=curr; + curr=curr.next; + } + if(prev!=null) + prev.next=newEntry; + } + //newEntry.next=curr; + //buckets[hash]=newEntry; + if(replace==false) + insertInList(newEntry); + //buckets[hash]=newEntry; + } + private void insertInList(Entry newEntry) + { + if(head==null) + { + head=newEntry; + tail=newEntry; + } + else + { + tail.after=newEntry; + newEntry.before=tail; + tail=newEntry; + } + } + public V get(K key) + { + int hash=hash(key); + Entry curr=buckets[hash]; + while(curr!=null) + { + if(curr.key.equals(key)) + { + return curr.value; + } + curr=curr.next; + } + return null; + } + + public void print() + { + Entrycurr=head; + while(curr!=null) + { + System.out.println("key is "+ curr.key+"val is "+ curr.value+"->"); + curr=curr.after; + } + } + private int hash(K key){ + return Math.abs(key.hashCode()) % capacity; + } + + public void remove(K key) + { + int hash=hash(key); + Entrycurr=buckets[hash]; + if(curr==null)//no exist + { + return; + } + Entryp=null; + Entryn; + while(curr!=null) + { + n=curr.next; + if(curr.key.equals(key)) + { + if(p==null)//first + { + buckets[hash]=buckets[hash].next; + } + else + { + p.next=n; + } + //adjust Linked List + adjustList(curr); + break; + } + p=curr; + curr=n; + } + + } + private void adjustList(Entry curr) + { + if(curr==head) + { + head=head.after; + if(head==null) + { + tail=null; + } + } + else if (curr==tail) + { + tail=tail.before; + tail.after=null; + } + else + { + curr.before.after=curr.after; + curr.after.before=curr.before; + } + } + public void deleteAll() + { + head=null; + tail=null; + for(int i=0;i { + private final Lock lock = new ReentrantLock(); + private final Condition notFull = lock.newCondition(); + private final Condition notEmpty = lock.newCondition(); + + private final Deque queue = new LinkedList(); + private final int limit; + + public MyBlockingQueue(int limit) { + if (limit < 1) throw new IllegalStateException("limit should be greater than 0"); + this.limit = limit; + } + + public synchronized void enqueue(T item) throws InterruptedException { + lock.lock(); + try { + while (queue.size() == limit) { + notFull.await(); + } + queue.addLast(item); + notEmpty.signal(); + } finally { + lock.unlock(); + } + } + + public synchronized T dequeue() throws InterruptedException { + lock.lock(); + try { + while (queue.isEmpty()) { + notEmpty.await(); + } + final T x = queue.removeFirst(); + notFull.signal(); + return x; + } finally { + lock.unlock(); + } + } +} \ No newline at end of file diff --git a/src/main/java/internals/QuadTree.java b/src/main/java/internals/QuadTree.java new file mode 100644 index 0000000..ce0d590 --- /dev/null +++ b/src/main/java/internals/QuadTree.java @@ -0,0 +1,95 @@ +package internals; + +import java.util.HashSet; +import java.util.Set; + +public class QuadTree

{ + + Point topLeft; + Point bottomRight; + Set> nodes; + // children (this can also be used like Trie, where it is an Array of QuadTree) + QuadTree

topLeftTree; + QuadTree

topRightTree; + QuadTree

bottomLeftTree; + QuadTree

bottomRightTree; + int maxLen; + private Set> node; + + public QuadTree(Point topLeft, Point bottomRight, int maxLen) { + this.topLeft = topLeft; + this.bottomRight = bottomRight; + this.maxLen = maxLen; + nodes = new HashSet<>(); + } + + public Set> search(Point p) { + QuadTree

curr = this; + + while (!curr.isLeaf()) { + + // recurse by checking if it is within the boundary + if (p.x < (curr.topLeft.x + curr.bottomRight.x) / 2) { + if (p.y < (curr.topLeft.y + curr.bottomRight.y) / 2) { + curr = curr.topLeftTree; + } else { + curr = curr.bottomLeftTree; + } + } else { + if (p.y < (curr.topLeft.y + curr.bottomRight.y) / 2) { + curr = curr.topRightTree; + } else { + curr = curr.bottomRightTree; + } + + } + } + return curr.node; + } + + private boolean isLeaf() { + return true; + } + + + static class QuadNode { + T data; + Point point; + + public QuadNode(Point p, T data) { + this.data = data; + this.point = p; + } + + public QuadNode(int x, int y, T data) { + this.data = data; + this.point = new Point(x, y); + } + + @Override + public String toString() { + return "data " + data + " point " + point; + } + } + + static class Point { + int x; + int y; + + public Point(int x, int y) { + this.x = x; + this.y = y; + } + + // more like a deep clone + public Point(Point p) { + this.x = p.x; + this.y = p.y; + } + + @Override + public String toString() { + return "x: " + x + " y: " + y; + } + } +} diff --git a/src/main/java/internals/SJUArrayList.java b/src/main/java/internals/SJUArrayList.java new file mode 100644 index 0000000..6a7d0c9 --- /dev/null +++ b/src/main/java/internals/SJUArrayList.java @@ -0,0 +1,166 @@ +package internals; + +public class SJUArrayList { + // Data fields + private E[] theData; + private int size = 0; + private int capacity = 0; + + // Constants + private static final int INIT_CAPACITY = 10; + + // Constructors + public SJUArrayList() { + this(INIT_CAPACITY); + } + + public SJUArrayList(int initCapacity) { + capacity = initCapacity; + theData = (E[]) new Object[capacity]; + } + + // Methods + public boolean add(E e) { + if(e == null) { + throw new NullPointerException(); + } + + if(size == capacity) { + reallocate(); + } + + theData[size] = e; + size++; + + return true; + } // End add(E e) method + + public void add(int index, E e) { + if(index < 0 || index > size) { + throw new ArrayIndexOutOfBoundsException(index); + } + + if(e == null) { + throw new NullPointerException(); + } + + if(size == capacity) { + reallocate(); + } + + for(int i = size; i > index; i--) { + theData[i] = theData[i - 1]; + } + + theData[index] = e; + size++; + } // End add(int index, E e) method + + public void clear() { + theData = (E[]) new Object[capacity]; + size = 0; + } // End clear() method + + public boolean equals(Object o) { + if(o == null) { + return false; + } + + if(getClass() != o.getClass()) { + return false; + } + + SJUArrayList otherO = (SJUArrayList) o; + + if(size != otherO.size) { + return false; + } + + for(int i = 0; i < size; i++) { + if(!theData[i].equals(otherO.theData[i])) { + return false; + } + } + + return true; + } // End equals(Object o) method + + public E get(int index) { + if(index < 0 || index >= size) { + throw new ArrayIndexOutOfBoundsException(index); + } + return theData[index]; + } // End get(int index) method + + public int indexOf(Object o) { + if(o == null) { + throw new NullPointerException(); + } + + for(int i = 0; i < size; i++) { + if(theData[i].equals(o)) { + return i; + } + } + + return -1; + } // End indexOf(Object o) method + + public boolean isEmpty() { + return size == 0; + } // End isEmpty() method + + public E remove(int index) { + if(index < 0 || index >= size) { + throw new ArrayIndexOutOfBoundsException(index); + } + + E temp = theData[index]; + + for(int i = index + 1; i < size; i++) { + theData[i - 1] = theData[i]; + } + + size--; + return temp; + } // End remove(int index) method + + public boolean remove(Object o) { + int indexOfO = indexOf(o); + + if(indexOfO == -1) { + return false; + } + + remove(indexOfO); + return true; + } // End remove(Object o) method + + public E set(int index, E e) { + if(index < 0 || index >= size) { + throw new ArrayIndexOutOfBoundsException(index); + } + + if(e == null) { + throw new NullPointerException(); + } + E temp = theData[index]; + theData[index] = e; + return temp; + } // End set(int index, E e) method + + public int size() { + return size; + } // End size() method + + private void reallocate() { + capacity *= 2; + E[] newArraylist = (E[]) new Object[capacity]; + + for(int i = 0; i < size; i++) { + newArraylist[i] = theData[i]; + } + + theData = newArraylist; + } // End reallocate() method +} \ No newline at end of file diff --git a/src/main/java/internals/SkipList.java b/src/main/java/internals/SkipList.java new file mode 100644 index 0000000..0baf132 --- /dev/null +++ b/src/main/java/internals/SkipList.java @@ -0,0 +1,189 @@ +package internals; + +import java.util.Random; + +interface SkippableList> { + + int LEVELS = 5; + + boolean delete(T target); + + void print(); + + void insert(T data); + + SkipNode search(T data); +} + +class SkipNode> { + + N data; + @SuppressWarnings("unchecked") + SkipNode[] next = (SkipNode[]) new SkipNode[SkippableList.LEVELS]; + + SkipNode(N data) { + this.data = data; + } + + void refreshAfterDelete(int level) { + SkipNode current = this; + while (current != null && current.getNext(level) != null) { + if (current.getNext(level).data == null) { + SkipNode successor = current.getNext(level).getNext(level); + current.setNext(successor, level); + return; + } + + current = current.getNext(level); + } + } + + void setNext(SkipNode next, int level) { + this.next[level] = next; + } + + SkipNode getNext(int level) { + return this.next[level]; + } + + SkipNode search(N data, int level, boolean print) { + if (print) { + System.out.print("Searching for: " + data + " at "); + print(level); + } + + SkipNode result = null; + SkipNode current = this.getNext(level); + while (current != null && current.data.compareTo(data) < 1) { + if (current.data.equals(data)) { + result = current; + break; + } + + current = current.getNext(level); + } + + return result; + } + + void insert(SkipNode skipNode, int level) { + SkipNode current = this.getNext(level); + if (current == null) { + this.setNext(skipNode, level); + return; + } + + if (skipNode.data.compareTo(current.data) < 1) { + this.setNext(skipNode, level); + skipNode.setNext(current, level); + return; + } + + while (current.getNext(level) != null && current.data.compareTo(skipNode.data) < 1 && + current.getNext(level).data.compareTo(skipNode.data) < 1) { + + current = current.getNext(level); + } + + SkipNode successor = current.getNext(level); + current.setNext(skipNode, level); + skipNode.setNext(successor, level); + } + + void print(int level) { + System.out.print("level " + level + ": [ "); + int length = 0; + SkipNode current = this.getNext(level); + while (current != null) { + length++; + System.out.print(current.data + " "); + current = current.getNext(level); + } + + System.out.println("], length: " + length); + } + +} + +public class SkipList> implements SkippableList { + + private final SkipNode head = new SkipNode<>(null); + private final Random rand = new Random(); + + @Override + public void insert(T data) { + SkipNode skipNode = new SkipNode<>(data); + for (int i = 0; i < LEVELS; i++) { + if (rand.nextInt((int) Math.pow(2, i)) == 0) { + //insert with prob = 1/(2^i) + insert(skipNode, i); + } + } + } + + @Override + public boolean delete(T target) { + System.out.println("Deleting " + target); + SkipNode victim = search(target, true); + if (victim == null) return false; + victim.data = null; + + for (int i = 0; i < LEVELS; i++) { + head.refreshAfterDelete(i); + } + + System.out.println("deleted..."); + return true; + } + + @Override + public SkipNode search(T data) { + return search(data, true); + } + + @Override + public void print() { + for (int i = LEVELS - 1; i >= 0; i--) { + head.print(i); + } + System.out.println(); + } + + private void insert(SkipNode SkipNode, int level) { + head.insert(SkipNode, level); + } + + private SkipNode search(T data, boolean print) { + SkipNode result = null; + for (int i = LEVELS - 1; i >= 0; i--) { + if ((result = head.search(data, i, print)) != null) { + if (print) { + System.out.println("Found " + data.toString() + " at level " + i + ", so stopped"); + System.out.println(); + } + break; + } + } + + return result; + } + + public static void main(String[] args) { + SkipList sl = new SkipList<>(); + int[] data = {4, 2, 7, 0, 9, 1, 3, 7, 3, 4, 5, 6, 0, 2, 8}; + for (int i : data) { + sl.insert(i); + } + + sl.print(); + sl.search(4); + + sl.delete(4); + + System.out.println("Inserting 10"); + sl.insert(10); + sl.print(); + sl.search(10); + } + +} diff --git a/src/main/java/internals/ThreadPool.java b/src/main/java/internals/ThreadPool.java new file mode 100644 index 0000000..751e9cd --- /dev/null +++ b/src/main/java/internals/ThreadPool.java @@ -0,0 +1,106 @@ +package internals; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.BlockingQueue; + +public class ThreadPool { + + private BlockingQueue taskQueue = null; + private final List runnables = new ArrayList<>(); + private boolean isStopped = false; + + public ThreadPool(int noOfThreads, int maxNoOfTasks) { + taskQueue = new ArrayBlockingQueue(maxNoOfTasks); + + for (int i = 0; i < noOfThreads; i++) { + PoolThreadRunnable poolThreadRunnable = + new PoolThreadRunnable(taskQueue); + + runnables.add(new PoolThreadRunnable(taskQueue)); + } + for (PoolThreadRunnable runnable : runnables) { + new Thread(runnable).start(); + } + } + + public synchronized void execute(Runnable task) throws Exception { + if (this.isStopped) throw + new IllegalStateException("ThreadPool is stopped"); + + this.taskQueue.offer(task); + } + + public synchronized void stop() { + this.isStopped = true; + for (PoolThreadRunnable runnable : runnables) { + runnable.doStop(); + } + } + + public synchronized void waitUntilAllTasksFinished() { + while (!this.taskQueue.isEmpty()) { + try { + Thread.sleep(1); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + static class PoolThreadRunnable implements Runnable { + + private Thread thread = null; + private BlockingQueue taskQueue = null; + private boolean isStopped = false; + + public PoolThreadRunnable(BlockingQueue queue) { + taskQueue = queue; + } + + public void run() { + this.thread = Thread.currentThread(); + while (!isStopped()) { + try { + Runnable runnable = (Runnable) taskQueue.take(); + runnable.run(); + } catch (Exception e) { + //log or otherwise report exception, + //but keep pool thread alive. + } + } + } + + public synchronized void doStop() { + isStopped = true; + //break pool thread out of dequeue() call. + this.thread.interrupt(); + } + + public synchronized boolean isStopped() { + return isStopped; + } + } + + + public static void main(String[] args) throws Exception { + + ThreadPool threadPool = new ThreadPool(3, 10); + + for (int i = 0; i < 10; i++) { + + int taskNo = i; + threadPool.execute(() -> { + String message = + Thread.currentThread().getName() + + ": Task " + taskNo; + System.out.println(message); + }); + } + + threadPool.waitUntilAllTasksFinished(); + threadPool.stop(); + + } +} \ No newline at end of file diff --git a/src/main/java/internals/TwitterSnowflakeUUID.java b/src/main/java/internals/TwitterSnowflakeUUID.java new file mode 100644 index 0000000..80982d7 --- /dev/null +++ b/src/main/java/internals/TwitterSnowflakeUUID.java @@ -0,0 +1,119 @@ +package internals; + +import java.net.NetworkInterface; +import java.security.SecureRandom; +import java.time.Instant; +import java.util.Enumeration; + +public class TwitterSnowflakeUUID { + + /** + * Distributed Sequence Generator. + * Inspired by Twitter snowflake: https://github.com/twitter/snowflake/tree/snowflake-2010 + *

+ * This class should be used as a Singleton. + * Make sure that you create and reuse a Single instance of SequenceGenerator per node in your distributed system cluster. + */ + private static final int UNUSED_BITS = 1; // Sign bit, Unused (always set to 0) + private static final int EPOCH_BITS = 41; + private static final int NODE_ID_BITS = 10; + private static final int SEQUENCE_BITS = 12; + + private static final int maxNodeId = (int) (Math.pow(2, NODE_ID_BITS) - 1); + private static final int maxSequence = (int) (Math.pow(2, SEQUENCE_BITS) - 1); + + // Custom Epoch (January 1, 2015 Midnight UTC = 2015-01-01T00:00:00Z) + private static final long CUSTOM_EPOCH = 1420070400000L; + + private final int nodeId; + + private volatile long lastTimestamp = -1L; + private volatile long sequence = 0L; + + // Create SequenceGenerator with a nodeId + public TwitterSnowflakeUUID(int nodeId) { + if (nodeId < 0 || nodeId > maxNodeId) { + throw new IllegalArgumentException(String.format("NodeId must be between %d and %d", 0, maxNodeId)); + } + this.nodeId = nodeId; + } + + // Let SequenceGenerator generate a nodeId + public TwitterSnowflakeUUID() { + this.nodeId = createNodeId(); + } + + public synchronized long nextId() { + long currentTimestamp = timestamp(); + + if (currentTimestamp < lastTimestamp) { + throw new IllegalStateException("Invalid System Clock!"); + } + + if (currentTimestamp == lastTimestamp) { + sequence = (sequence + 1) & maxSequence; + if (sequence == 0) { + // Sequence Exhausted, wait till next millisecond. + currentTimestamp = waitNextMillis(currentTimestamp); + } + } else { + // reset sequence to start with zero for the next millisecond + sequence = 0; + } + + lastTimestamp = currentTimestamp; + + //Now, the first 41 bits of the ID (after the signed bit) will be filled with the epoch timestamp. Let’s do that using a left-shift - + long id = currentTimestamp << (NODE_ID_BITS + SEQUENCE_BITS); + + //Next, we take the configured node ID and fill the next 10 bits with the node ID. Let’s say that the nodeId is 786 - + //id |= nodeId << 12 + id |= (nodeId << SEQUENCE_BITS); + + //Finally, we fill the last 12 bits with the local counter. + // Considering the counter’s next value is 3450, i.e. sequence = 3450, the final ID is obtained like so - + id |= sequence; + return id; + } + + + // Get current timestamp in milliseconds, adjust for the custom epoch. + private static long timestamp() { + /** + * Let’s now understand how it works. Let’s say it’s June 9, 2018 10:00:00 AM GMT. The epoch timestamp for this particular time is 1528538400000. + * First, we adjust our timestamp with respect to the custom epoch- + * currentTimestamp = 1528538400000 - 1420070400000 + */ + return Instant.now().toEpochMilli() - CUSTOM_EPOCH; + } + + // Block and wait till next millisecond + private long waitNextMillis(long currentTimestamp) { + while (currentTimestamp == lastTimestamp) { + currentTimestamp = timestamp(); + } + return currentTimestamp; + } + + private int createNodeId() { + int nodeId; + try { + StringBuilder sb = new StringBuilder(); + Enumeration networkInterfaces = NetworkInterface.getNetworkInterfaces(); + while (networkInterfaces.hasMoreElements()) { + NetworkInterface networkInterface = networkInterfaces.nextElement(); + byte[] mac = networkInterface.getHardwareAddress(); + if (mac != null) { + for (int i = 0; i < mac.length; i++) { + sb.append(String.format("%02X", mac[i])); + } + } + } + nodeId = sb.toString().hashCode(); + } catch (Exception ex) { + nodeId = (new SecureRandom().nextInt()); + } + nodeId = nodeId & maxNodeId; + return nodeId; + } +} diff --git a/src/main/java/java8/Books.txt b/src/main/java/java8/Books.txt new file mode 100644 index 0000000..7d652e6 --- /dev/null +++ b/src/main/java/java8/Books.txt @@ -0,0 +1,24 @@ +Gulliver's Travels  +Jonathan Swift +Fantasy Fiction +4 +Frankenstein +Mary Shelley +Science Fiction +4 +The Woman in White  +Wilkie Collins +Mystery +4.1 +Alice's Adventures In Wonderland  +Lewis Carroll +Fairy tale +4.1 +Three Men in a Boat  +Jerome K. Jerome +Humorous Fiction +4 +Brave New World  +Aldous Huxley +Science Fiction +4.2 \ No newline at end of file diff --git a/src/main/java/java8/CustomCollectors.java b/src/main/java/java8/CustomCollectors.java new file mode 100644 index 0000000..41ebacb --- /dev/null +++ b/src/main/java/java8/CustomCollectors.java @@ -0,0 +1,20 @@ +package java8; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collector; +import java.util.stream.Collectors; + +public class CustomCollectors { + public static void main(String[] args) { + List numbers = List.of(2,6,8,9,0,1,52,5,61,8,9,96,0,18,23); + + Collector,List> toList= Collector.of(ArrayList::new, //supplier + (list,e)->list.add(e), // Accumulator, List::add + (list1,list2)-> {list1.addAll(list2); return list1;}, // combiner + Collector.Characteristics.IDENTITY_FINISH + + ); + numbers.stream().filter(e->e%2==0).collect(toList); + } +} diff --git a/src/main/java/java8/CustomSpliterator.java b/src/main/java/java8/CustomSpliterator.java new file mode 100644 index 0000000..2cc3f71 --- /dev/null +++ b/src/main/java/java8/CustomSpliterator.java @@ -0,0 +1,314 @@ +package java8; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.sql.Date; +import java.util.*; +import java.util.function.Consumer; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; + +public class CustomSpliterator { + public static void main(String[] args) throws IOException { + Path path = Paths.get("/Users/vignesh_rajarajan/Documents/learning/Problems/src/java8/Books.txt"); + try(Stream lines = Files.lines(path)){ + Spliterator baseSpliterator = lines.spliterator(); + Spliterator spliterator= new BookSpliterator(baseSpliterator); + Stream stream=StreamSupport.stream(spliterator,false); + stream.forEach(System.out::println); + + } + + Path path1 = Paths.get("/Users/vignesh_rajarajan/Documents/learning/Problems/src/java8/EmployeeData.txt"); + + try(Stream lines = Files.lines(path1)){ + Stream words= lines.flatMap(line-> Arrays.stream(line.split(","))); + Spliterator baseSpliterator = words.spliterator(); + Spliterator spliterator= new EmployeeSpliterator(baseSpliterator); + Stream empStream=StreamSupport.stream(spliterator,false); + + List employeeList= empStream.collect(Collectors.toList()); + + System.out.println(employeeList.stream().map(Employee::getName).collect(Collectors.joining(","+"\n"))); + + TreeSet tree=employeeList.stream().collect(Collectors.toCollection(TreeSet::new)); + + Map map=employeeList.stream().collect(Collectors.toMap(Employee::getId,Employee::getName)); + + Map> partitionedList=employeeList.stream().collect(Collectors.partitioningBy(e->e.getGender()=='M')); + + Map> groupByDesignation= employeeList.stream().collect(Collectors.groupingBy(Employee::getDesignation)); + } + } +} + +class Book{ + private String name; + private String author; + private String genre; + private Double rating; + + public Book(String name, String author, String genre, Double rating) { + this.name = name; + this.author = author; + this.genre = genre; + this.rating = rating; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getAuthor() { + return author; + } + + public void setAuthor(String author) { + this.author = author; + } + + public String getGenre() { + return genre; + } + + public void setGenre(String genre) { + this.genre = genre; + } + + public Double getRating() { + return rating; + } + + public void setRating(Double rating) { + this.rating = rating; + } + + @Override + public String toString() { + return "Book{" + + "name='" + name + '\'' + + ", author='" + author + '\'' + + ", genre='" + genre + '\'' + + ", rating=" + rating + + '}'; + } +} + +class BookSpliterator implements Spliterator{ + + private String name; + private String author; + private String genre; + private Double rating; + private Spliterator baseSpliterator; + + public BookSpliterator(Spliterator baseSpliterator) { + this.baseSpliterator = baseSpliterator; + } + + @Override + public boolean tryAdvance(Consumer action) { + if(this.baseSpliterator.tryAdvance(name ->this.name=name ) && + this.baseSpliterator.tryAdvance(author->this.author=author) && + this.baseSpliterator.tryAdvance(genre->this.genre=genre) && + this.baseSpliterator.tryAdvance(rating->this.rating=Double.valueOf(rating)) + ){ + action.accept(new Book(this.name, this.author,this.genre,this.rating)); + return true; + } + return false; + } + + @Override + public Spliterator trySplit() { + return null; + } + + @Override + public long estimateSize() { + return baseSpliterator.estimateSize()/4; // because the book txt has 4 lines for each book + } + + @Override + public int characteristics() { + return this.baseSpliterator.characteristics(); + } +} + + class Employee implements Comparable{ + + private int id; + private String name; + private char gender; + private Date dob; + private String city; + private String designation; + private Date joiningDate; + private double salary; + + public Employee(int id, String name, char gender, Date dob, String city, String designation, + Date joiningDate, double salary) { + this.id = id; + this.name = name; + this.gender = gender; + this.dob = dob; + this.city = city; + this.designation = designation; + this.joiningDate = joiningDate; + this.salary = salary; + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public char getGender() { + return gender; + } + + public void setGender(char gender) { + this.gender = gender; + } + + public Date getDob() { + return dob; + } + + public void setDob(Date dob) { + this.dob = dob; + } + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getDesignation() { + return designation; + } + + public void setDesignation(String designation) { + this.designation = designation; + } + + public Date getJoiningDate() { + return joiningDate; + } + + public void setJoiningDate(Date joiningDate) { + this.joiningDate = joiningDate; + } + + public double getSalary() { + return salary; + } + + public void setSalary(double salary) { + this.salary = salary; + } + + @Override + public String toString() { + return "Employee [id=" + id + ", name=" + name + ", gender=" + gender + ", dob=" + dob + ", city=" + city + + ", designation=" + designation + ", joiningDate=" + joiningDate + ", salary=" + salary + "]"; + } + + @Override + public int compareTo(Employee o) { + + if(this.id < o.id) + return -1; + else if(this.id > o.id) + return 1; + else + return 0; + } + +} + +class EmployeeSpliterator implements Spliterator { + + private Spliterator wordSpliterator; + private int id; + private String name; + private char gender; + private Date dob; + private String city; + private String designation; + private Date joiningDate; + private double salary; + + + + + public EmployeeSpliterator(Spliterator wordSpliterator) { + this.wordSpliterator = wordSpliterator; + } + + @Override + public boolean tryAdvance(Consumer action) { + + if(this.wordSpliterator.tryAdvance(word -> this.id = Integer.valueOf(word)) + && this.wordSpliterator.tryAdvance(word -> this.name = word) + && this.wordSpliterator.tryAdvance(word -> this.gender = word.charAt(0)) + && this.wordSpliterator.tryAdvance(word -> this.dob = Date.valueOf(word)) + && this.wordSpliterator.tryAdvance(word -> this.city = word) + && this.wordSpliterator.tryAdvance(word -> this.designation = word) + && this.wordSpliterator.tryAdvance(word -> this.joiningDate = Date.valueOf(word)) + && this.wordSpliterator.tryAdvance(word -> this.salary = Double.valueOf(word)) + ) { + action.accept(new Employee(this.id, + this.name, + this.gender, + this.dob, + this.city, + this.designation, + this.joiningDate, + this.salary + )); + return true; + } + + return false; + } + + @Override + public Spliterator trySplit() { + return null; + } + + @Override + public long estimateSize() { + return wordSpliterator.estimateSize()/8; + } + + @Override + public int characteristics() { + return wordSpliterator.characteristics(); + } + +} + + diff --git a/src/main/java/java8/DesignPatternJava8.java b/src/main/java/java8/DesignPatternJava8.java new file mode 100644 index 0000000..9ded820 --- /dev/null +++ b/src/main/java/java8/DesignPatternJava8.java @@ -0,0 +1,76 @@ +package java8; + +import java.util.function.Function; +import java.util.function.UnaryOperator; + +public class DesignPatternJava8 { + public static void main(String[] args) { + ArrayListIteratorPattern listFn= new ArrayListIteratorPattern(5); + listFn.setElements(new Object[]{1,2,3,4,5,6,7}); + listFn.forEach(System.out::println); + + Burger bur=new BurgerShop(burger -> burger.addCheese()).use(new BurgerShop(Burger::addVeggies).use(new Burger())); + System.out.println(bur); + } +} + +class ArrayListIteratorPattern { + Object[] elements ; + public ArrayListIteratorPattern(int n){ + elements = new Object[n]; + } + + public void setElements(Object[] elements){ + this.elements=elements; + } + + public void forEach(Consumer consumer){ + for (Object element : elements) { + consumer.accept(element); + } + } +} + + class Burger { + + private String burgerType; + + public Burger() { + + this.burgerType = ""; + } + + private Burger(String type) { + this.burgerType = type; + } + + public Burger addVeggies() { + System.out.println("Adding vegies to the burger"); + return new Burger(this.burgerType += " Veggie"); + } + + public Burger addCheese() { + System.out.println("Adding cheese to the burger"); + return new Burger(this.burgerType += " Cheese"); + } + + public String toString() { + return String.format("%s", burgerType + " burger"); + } + +} + +class BurgerShop { + + UnaryOperator< Burger> decoration; + + public BurgerShop(UnaryOperator decoration) { + this.decoration = decoration; + } + + public Burger use(Burger baseBurger) { + System.out.println("Base Burger : " + baseBurger); + return decoration.apply(baseBurger); + } + +} diff --git a/src/main/java/java8/EmployeeData.txt b/src/main/java/java8/EmployeeData.txt new file mode 100644 index 0000000..d752b0b --- /dev/null +++ b/src/main/java/java8/EmployeeData.txt @@ -0,0 +1,10 @@ +27827,Richard,M,1988-06-10,Boston,Developer,2017-12-12,60000.00 +27828,John,M,1978-07-11,Boston,Architect,2015-01-01,150000.00 +27829,David,M,1982-09-12,Newyork City,Manager,2015-01-19,145000.00 +27830,Meenal,F,1991-01-13,Austin,Developer,2018-07-05,65000.00 +27831,Ginni,F,1993-06-14,Delhi,Developer,2017-12-12,72000.00 +27832,Tom,M,1988-03-15,Banglore,Developer,2019-01-10,69000.00 +27833,Michael,M,1984-04-16,London,Lead,2018-09-21,87000.00 +27834,Alexa,F,1995-10-29,Newyork City,Developer,2019-10-11,65000.00 +27835,Peter,M,1993-02-09,London,Developer,2019-10-11,63000.00 +27836,Parvati,F,1991-12-14,Jaipur,Lead,2017-06-10,89000.00 \ No newline at end of file diff --git a/src/main/java/java8/FunctionalInterfaceExamples.java b/src/main/java/java8/FunctionalInterfaceExamples.java new file mode 100644 index 0000000..034db4a --- /dev/null +++ b/src/main/java/java8/FunctionalInterfaceExamples.java @@ -0,0 +1,107 @@ +package java8; + +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.function.Function; + +public class FunctionalInterfaceExamples { + + public static void main(String[] args) { +// FuncInter inter = s -> s.toLowerCase(Locale.ROOT); +// System.out.println(inter.execute("aDkle")); +// +// List names = List.of("anakjc", "cmnsd c", "bdcskjb"); +// Function intfn = String::length; +// List newList = map(names, intfn); +// System.out.println(newList); + +// System.out.println(createFactory(()->Math.random()*100, Double::intValue).create()) + + Consumer c1= s-> System.out.println(s+""+Math.random()*100); + Consumer c2= s-> System.out.println(s+""+Math.random()*99); + Consumer c3= c1.thenAccept(c2); + c3.accept("Hi : "); + + Functional f1= Square::getArea; + Functional f2= Math::sqrt; + + Functional f3= f2.compose(f1); + System.out.println(f3.apply(new Square(10))); + + //currying + Functional>> func= u->v->w->u+v+w; + System.out.println(func.apply(3).apply(4).apply(6)); + + + + } + + private static List map(List names, Function intfn) { + List newList = new ArrayList<>(); + for (T name : names) { + newList.add(intfn.apply(name)); + } + return newList; + } + + public static IFactory createFactory(IProducer producer, IConfigurator configurator) { + return () -> { + return configurator.configure(producer.produce()); + }; + } + +} + +interface FuncInter { + R execute(T t); +} + +interface IFactory { + T create(); +} + +interface IProducer { + T produce(); +} + +interface IConfigurator { + R configure(T t); + +} + +interface Consumer { + void accept(T t); + default Consumer thenAccept(Consumer next){ + return (T t)->{ + this.accept(t); + next.accept(t); + }; + } +} + +@FunctionalInterface +interface Functional{ + R apply(T t); + + default Functional compose(Functional before){ + return (V v)-> this.apply(before.apply(v)); + } +} + +class Square{ + private int area; + + Square(int area){ + this.area=area; + } + public int getArea() { + return area; + } + + public void setArea(int area) { + this.area = area; + } +} + diff --git a/src/main/java/linkedLists/Agoda.java b/src/main/java/linkedLists/Agoda.java new file mode 100644 index 0000000..7095fc5 --- /dev/null +++ b/src/main/java/linkedLists/Agoda.java @@ -0,0 +1,113 @@ +package linkedLists; + +import java.util.Deque; +import java.util.LinkedList; + +public class Agoda { + + Node head; + + public static void main(String[] args) { + Node 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); + head.next.next.next.next.next = new Node(6); + + Agoda agoda = new Agoda(); + agoda.slidingWindow(); + } + + private void slidingWindow() { + int[] arr = { 6, 4, 5, 2, 1, 9, 8 }; + int index = 0; + int k = 3; + Deque deque = new LinkedList(); + for (; index < k; index++) { + while (!deque.isEmpty() && arr[deque.peekLast()] < arr[index]) + deque.removeLast(); + deque.addLast(index); + } + + for (; index < arr.length; index++) { + System.out.println(arr[deque.peekFirst()]); + while (!deque.isEmpty() && index - k >= deque.peekFirst()) + deque.removeFirst(); + + while (!deque.isEmpty() && arr[deque.peekLast()] < arr[index]) + deque.removeLast(); + + deque.addLast(index); + } + + } + + private void reverseKNode(Node node, Node link) { + + Node curr = node; + Node prev = null; + Node next = null; + + int index = 0; + while (curr != null && index < 3) { + next = curr.next; + curr.next = prev; + prev = curr; + curr = next; + index++; + } + + if (link == null) + head = prev; + else + link.next = prev; + + if (curr != null) + reverseKNode(curr, node); + + while (head != null) { + System.out.println(head.data); + head = head.next; + } + } + + private void reversePairNodes(Node head) { + Node temp = null; + Node realHead = null; + while (head != null && head.next != null) { + if (temp != null) + temp.next.next = head.next; + + temp = head.next; + head.next = head.next.next; + temp.next = head; + if (realHead == null) + realHead = temp; + head = head.next; + } + while (realHead != null) { + System.out.println(realHead.data); + realHead = realHead.next; + } + } + + private void findNthNodeFromEnd(Node head, int n) { + if (head == null || head.next == null) + return; + Node slowPointer = head; + Node fastPointer = head; + int index = 0; + while (n > index && fastPointer != null) { + fastPointer = fastPointer.next; + index++; + } + + while (fastPointer.next != null) { + slowPointer = slowPointer.next; + fastPointer = fastPointer.next; + } + System.out.println(slowPointer.data); + } + +} diff --git a/src/main/java/linkedLists/AllProblems.java b/src/main/java/linkedLists/AllProblems.java new file mode 100644 index 0000000..109679c --- /dev/null +++ b/src/main/java/linkedLists/AllProblems.java @@ -0,0 +1,257 @@ +package linkedLists; + +public class AllProblems { + + static Node head; + static int size; + static RandomListNode randomHead; + + public static void main(String[] args) { + addNode(2); + addNode(5); + addNode(8); + addNode(10); + addNode(12); + // findNthNode(3); + // findCyclic(); + // reverseLinkedList(); + // middleOfTheLinkedList(); + // printListInReverse(head); + // reversePairLinkedListIterative(); + // reverseLinkedListUsingRecursiveMethod(head); + // cloneRandomLinkedList(); + // printList(); + // reverseLinkedListPractise(); + Node head2 = new Node(1); + head2.next = new Node(4); + head2.next.next = new Node(7); + head2.next.next = new Node(9); + head2.next.next.next = new Node(19); + head2.next.next.next.next = new Node(29); + Node result = mergeList(head, head2); + + while (result != null) { + System.out.print(result.data + " "); + result = result.next; + } + } + + private static Node mergeList(Node a, Node b) { + System.out.println(a + "->" + b); + Node c = null; + if (a == null) + return b; + if (b == null) + return a; + + if (a.data < b.data) { + c = a; + c.next = mergeList(a.next, b); + } else { + c = b; + c.next = mergeList(b.next, a); + } + return c; + } + + private static void cloneRandomLinkedList() { + + RandomListNode node1 = new RandomListNode(1); + RandomListNode node2 = new RandomListNode(2); + RandomListNode node3 = new RandomListNode(3); + RandomListNode node4 = new RandomListNode(4); + RandomListNode node5 = new RandomListNode(5); + + randomHead = node1; + + node1.next = node2; + node2.next = node3; + node3.next = node4; + node4.next = node5; + + node1.random = node3; + node2.random = node1; + node3.random = node5; + node4.random = node3; + node5.random = node2; + + // create cloned node between original nodes + RandomListNode temp = randomHead; + while (null != temp) { + RandomListNode rn = new RandomListNode(temp.data + 0.5); + rn.next = temp.next; + temp.next = rn; + temp = rn.next; + } + RandomListNode assignTemp = randomHead; + while (null != assignTemp) { + assignTemp.next.random = assignTemp.random.next; + + assignTemp = assignTemp.next; + } + + temp = randomHead; + while (null != temp && null != temp.next) { + assignTemp.next.next = assignTemp.next.next.next; + + } + + RandomListNode tempp = randomHead; + while (tempp != null) { + System.out.println(tempp.data); + tempp = tempp.next; + } + + } + + private static Node reverseLinkedListUsingRecursiveMethod(Node curr) { + + if (null == curr) + return null; + + Node prev = reverseLinkedListUsingRecursiveMethod(curr.next); + if (null != prev) + prev.next = curr; + else + head = curr; + return curr; + + } + + private static void reversePairLinkedListIterative() { + + Node temp1 = null; + Node realHead = null; + while (head != null && head.next != null) { + if (temp1 != null) + temp1.next.next = head.next; + + temp1 = head.next; + head.next = head.next.next; + temp1.next = head; + if (realHead == null) + realHead = temp1; + head = head.next; + } + while (realHead != null) { + System.out.println(realHead.data); + realHead = realHead.next; + } + } + + private static Object printListInReverse(Node temp) { + if (temp != null) { + printListInReverse(temp.getNode()); + System.out.println(temp.getData()); + } + return null; + } + + private static void middleOfTheLinkedList() { + + Node fastPointer = head; + Node slowPointer = head; + while (fastPointer.getNode() != null && fastPointer.getNode().getNode() != null) { + slowPointer = slowPointer.getNode(); + fastPointer = fastPointer.getNode().getNode(); + if (fastPointer.getNode() == null || fastPointer.getNode().getNode() == null) { + System.out.println(slowPointer.getData()); + } + } + } + + private static void reverseLinkedList() { + Node curr = head; + Node prev = curr; + if (head.getNode() != null) { + curr = curr.getNode(); + } + while (curr.getNode() != null) { + Node next = curr.getNode(); + curr.setNode(prev); + prev = curr; + curr = next; + } + curr.setNode(prev); + head = curr; + } + + private static void reverseLinkedListPractise() { + Node curr = head; + Node prev = null; + Node next = null; + while (curr != null) { + next = curr.next; + curr.next = prev; + prev = curr; + curr = next; + } + curr = prev; + while (curr != null) { + System.out.println(curr.data); + curr = curr.next; + } + } + + private static void findCyclic() { + Node slowPointer = head; + Node fastPointer = head; + System.out.println("slow pointer : " + slowPointer.getData() + "and Fast pointer :" + fastPointer.getData()); + while (fastPointer.getNode() != null && fastPointer.getNode().getNode() != null) { + slowPointer = slowPointer.getNode(); + fastPointer = fastPointer.getNode().getNode(); + if (slowPointer == fastPointer) { + System.out.println("Gotcha its cyclic" + slowPointer + "::" + fastPointer); + return; + } else { + System.out.println("slow pointer : " + slowPointer + "and Fast pointer :" + fastPointer); + } + } + } + + private static void findNthNode(int n) { + Node temp = head; + Node nthNode = head; + int index = 1; + while (index < n) { + temp = temp.getNode(); + index++; + } + + while (temp.getNode() != null) { + temp = temp.getNode(); + nthNode = nthNode.getNode(); + } + + System.out.println("Nth Node from end: " + nthNode.getData()); + + } + + private static void printList() { + Node temp = head; + int index = 0; + while (index < size) { + System.out.println(temp.getData()); + temp = temp.getNode(); + index++; + } + } + + private static void addNode(int data) { + Node node = new Node(data); + if (head == null) { + head = node; + node.setNode(null); + } else { + Node temp = head; + int index = 1; + while (index < size) { + temp = temp.getNode(); + index++; + } + temp.setNode(node); + node.setNode(null); + } + size++; + } +} diff --git a/src/main/java/linkedLists/ArrayToBST.java b/src/main/java/linkedLists/ArrayToBST.java new file mode 100644 index 0000000..c39029a --- /dev/null +++ b/src/main/java/linkedLists/ArrayToBST.java @@ -0,0 +1,34 @@ +package linkedLists; + +public class ArrayToBST { + + static int i; + static int[] arr = { 1, 2, 3, 4, 5, 6, 7 }; + + Node sortedArrayToBST(int arr[], int start, int end) { + + if (start > end) { + return null; + } + int mid = (start + end) / 2; + Node node = new Node(arr[mid]); + node.prev = sortedArrayToBST(arr, start, mid - 1); + node.next = sortedArrayToBST(arr, mid + 1, end); + + return node; + } + + public static void main(String[] args) { + ArrayToBST arrToBSt = new ArrayToBST(); + Node sortedArrayToBST = arrToBSt.sortedArrayToBST(arr, 0, 6); + preOrder(sortedArrayToBST); + } + + private static void preOrder(Node converArrayToBST) { + if (null == converArrayToBST) + return; + preOrder(converArrayToBST.prev); + System.out.print(converArrayToBST.getData() + " "); + preOrder(converArrayToBST.next); + } +} diff --git a/src/main/java/linkedLists/CloneRandomPointerLinkedList.java b/src/main/java/linkedLists/CloneRandomPointerLinkedList.java new file mode 100644 index 0000000..1bd7fed --- /dev/null +++ b/src/main/java/linkedLists/CloneRandomPointerLinkedList.java @@ -0,0 +1,125 @@ +package linkedLists; + +import java.util.HashMap; +import java.util.Map; + +// Java program to clone a linked list with next +// and arbit pointers in O(n) time +class CloneRandomPointerLinkedList { + + // Structure of linked list Node + static class Node { + int data; + Node next, random; + + Node(int x) { + data = x; + next = random = null; + } + } + + // Utility function to print the list. + static void print(Node start) { + Node ptr = start; + while (ptr != null) { + System.out.println("Data = " + ptr.data + ", Random = " + ptr.random.data); + ptr = ptr.next; + } + } + + // This function clones a given + // linked list in O(1) space + static Node clone(Node start) { + if(start==null) return null; + Node curr = start, temp = null; + + // insert additional node after + // every node of original list + while (curr != null) { + temp = curr.next; + + // Inserting node + curr.next = new Node(curr.data); + curr.next.next = temp; + curr = temp; + } + curr = start; + + // adjust the random pointers of the + // newly added nodes + while (curr != null && curr.next!=null) { + if (curr.random != null) + curr.next.random = curr.random.next; + // move to the next newly added node by + // skipping an original node + curr =curr.next.next; + } + + Node original = start, copy = start.next; + + // save the start of copied linked list + temp = copy; + + // now separate the original list and copied list + while (original != null && copy != null) { + original.next = (original.next != null) ? original.next.next : original.next; + + copy.next = (copy.next != null) ? copy.next.next : copy.next; + original = original.next; + copy = copy.next; + } + return temp; + } + + public RandomListNode copyRandomList(RandomListNode head) { + if (head == null) { + return null; + } + + final Map map = new HashMap<>(); + + RandomListNode cur = head; + while(cur != null) { + map.put(cur, new RandomListNode(cur.data)); + cur = cur.next; + } + + for (Map.Entry entry : map.entrySet()) { + final RandomListNode newNode = entry.getValue(); + newNode.next = map.get(entry.getKey().next); + newNode.random = map.get(entry.getKey().random); + } + + return map.get(head); + } + + // Driver code + public static void main(String[] args) { + Node start = new Node(1); + start.next = new Node(2); + start.next.next = new Node(3); + start.next.next.next = new Node(4); + start.next.next.next.next = new Node(5); + + // 1's random points to 3 + start.random = start.next.next; + + // 2's random points to 1 + start.next.random = start; + + // 3's and 4's random points to 5 + start.next.next.random = start.next.next.next.next; + start.next.next.next.random = start.next.next.next.next; + + // 5's random points to 2 + start.next.next.next.next.random = start.next; + + System.out.println("Original list : "); + print(start); + + System.out.println("Cloned list : "); + Node cloned_list = clone(start); + print(cloned_list); + + } +} diff --git a/src/main/java/linkedLists/DLLToBBST.java b/src/main/java/linkedLists/DLLToBBST.java new file mode 100644 index 0000000..d8b75fc --- /dev/null +++ b/src/main/java/linkedLists/DLLToBBST.java @@ -0,0 +1,69 @@ +package linkedLists; + +public class DLLToBBST { + + static Node head; + + public static void main(String[] args) { + addNode(7); + addNode(6); + addNode(5); + addNode(4); + addNode(3); + addNode(2); + addNode(1); + + DLLToBBST bst = new DLLToBBST(); + bst.preOrder(bst.sortedListToBST()); + } + + Node sortedListToBST() { + int n = countNodes(head); + return sortedListToBSTRecur(n); + } + + Node sortedListToBSTRecur(int n) { + if (n <= 0) + return null; + Node left = sortedListToBSTRecur(n / 2); + Node root = head; + root.prev = left; + head = head.next; + root.next = sortedListToBSTRecur(n - n / 2 - 1); + + return root; + } + + private static void addNode(int i) { + + Node node = new Node(i); + if (null == head) { + head = node; + } else { + Node temp = head; + while (null != temp.next) { + temp = temp.next; + } + temp.next = node; + node.prev = temp; + } + } + + int countNodes(Node head) { + int count = 0; + Node temp = head; + while (temp != null) { + temp = temp.next; + count++; + } + return count; + } + + void preOrder(Node node) { + if (node == null) + return; + System.out.print(node.data + " "); + preOrder(node.prev); + preOrder(node.next); + } +} diff --git a/src/main/java/linkedLists/DLLToBBSTTimeEfficiency.java b/src/main/java/linkedLists/DLLToBBSTTimeEfficiency.java new file mode 100644 index 0000000..095e97d --- /dev/null +++ b/src/main/java/linkedLists/DLLToBBSTTimeEfficiency.java @@ -0,0 +1,79 @@ +package linkedLists; + +public class DLLToBBSTTimeEfficiency { + + static Node head; + + private Node findMiddleNode(Node temp) { + Node fastPointer, slowPointer; + fastPointer = slowPointer = temp; + + while (null != fastPointer && null != fastPointer.next) { + fastPointer = fastPointer.next.next; + slowPointer = slowPointer.next; + } + return slowPointer; + } + + private Node formBBST(Node head) { + if (null == head) + return null; + if (null == head.next) { + head.prev = null; + return head; + } + + Node root = findMiddleNode(head); + Node temp = head; + + while (root != temp.next) { + temp = temp.next; + } + temp.next = null; + + Node rightStart = root.next; + root.prev = formBBST(head); + root.next = formBBST(rightStart); + + return root; + } + + private static void addNode(int i) { + + Node node = new Node(i); + if (null == head) { + head = node; + } else { + Node temp = head; + while (null != temp.next) { + temp = temp.next; + } + temp.next = node; + node.prev = temp; + } + } + + public static void main(String[] args) { + addNode(7); + addNode(6); + addNode(5); + addNode(4); + addNode(3); + addNode(2); + addNode(1); + + DLLToBBSTTimeEfficiency bst = new DLLToBBSTTimeEfficiency(); + bst.preOrder(bst.formBBST(head)); + + } + + private void preOrder(Node temp) { + if (null == temp) + return; + + System.out.print(temp.getData() + " "); + preOrder(temp.prev); + preOrder(temp.next); + + } +} diff --git a/src/main/java/linkedLists/DetectAndRemoveLoop.java b/src/main/java/linkedLists/DetectAndRemoveLoop.java new file mode 100644 index 0000000..67ad625 --- /dev/null +++ b/src/main/java/linkedLists/DetectAndRemoveLoop.java @@ -0,0 +1,112 @@ +package linkedLists; +class DetectAndRemoveLoop { + + static Node head; + + static class Node { + + int data; + Node next; + + Node(int d) { + data = d; + next = null; + } + } + + // Function that detects loop in the list + int detectAndRemoveLoop(Node node) { + Node slow = node, fast = node; + while (slow != null && fast != null && fast.next != null) { + slow = slow.next; + fast = fast.next.next; + + // If slow and fast meet at same point then loop is present + if (slow == fast) { + removeLoop(slow, node); + return 1; + } + } + return 0; + } + + public Node detectCycle(Node head) { + if(head==null || head.next==null) return null; + + Node root=head; + Node slow=head; + Node fast=head; + + while(fast!=null && fast.next!=null){ + fast=fast.next.next; + slow=slow.next; + if(fast==slow){ + while(slow!=root){ + slow=slow.next; + root=root.next; + } + return slow; + } + + } + + return null; + + } + + // Function to remove loop + void removeLoop(Node loop, Node curr) { + Node ptr1 = null, ptr2 = null; + + /* Set a pointer to the beging of the Linked List and + move it one by one to find the first node which is + part of the Linked List */ + ptr1 = curr; + while (true) { + + /* Now start a pointer from loop_node and check if it ever + reaches ptr2 */ + ptr2 = loop; + while (ptr2.next != loop && ptr2.next != ptr1) { + ptr2 = ptr2.next; + } + + /* If ptr2 reahced ptr1 then there is a loop. So break the + loop */ + if (ptr2.next == ptr1) { + break; + } + + /* If ptr2 did't reach ptr1 then try the next node after ptr1 */ + ptr1 = ptr1.next; + } + + /* After the end of loop ptr2 is the last node of the loop. So + make next of ptr2 as NULL */ + ptr2.next = null; + } + + // Function to print the linked list + void printList(Node node) { + while (node != null) { + System.out.print(node.data + " "); + node = node.next; + } + } + + // Driver program to test above functions + public static void main(String[] args) { + DetectAndRemoveLoop list = new DetectAndRemoveLoop(); + list.head = new Node(1); + list.head.next = new Node(2); + list.head.next.next = new Node(3); + list.head.next.next.next = new Node(4); + list.head.next.next.next.next = new Node(5); + + // Creating a loop for testing + head.next.next.next.next.next = head.next; + list.detectAndRemoveLoop(head); + System.out.println("Linked List after removing loop : "); + list.printList(head); + } +} \ No newline at end of file diff --git a/src/main/java/linkedLists/FlattenLinkedList.java b/src/main/java/linkedLists/FlattenLinkedList.java new file mode 100644 index 0000000..ed21b40 --- /dev/null +++ b/src/main/java/linkedLists/FlattenLinkedList.java @@ -0,0 +1,124 @@ +package linkedLists; +class FlattenLinkedList +{ + Node head; + + class Node + { + int data; + Node right, down; + Node(int data) + { + this.data = data; + right = null; + down = null; + } + + @Override + public String toString() { + return "" + this.data; + } + } + + public Node flatten(Node root){ + if(root==null || root.right==null) return root; + Node right= flatten(root.right); + root= merge(root,right); + return root; + } + + public Node merge(Node root, Node right){ + Node dummy= new Node(0); + + Node head=dummy; + + while(root.down!=null && right.down!=null){ + if(root.data 10 -> 19 -> 28 + | | | | + V V V V + 7 20 22 35 + | | | + V V V + 8 50 40 + | | + V V + 30 45 + */ + + L.head = L.push(L.head, 30); + L.head = L.push(L.head, 8); + L.head = L.push(L.head, 7); + L.head = L.push(L.head, 5); + + L.head.right = L.push(L.head.right, 20); + L.head.right = L.push(L.head.right, 10); + + L.head.right.right = L.push(L.head.right.right, 50); + L.head.right.right = L.push(L.head.right.right, 22); + L.head.right.right = L.push(L.head.right.right, 19); + + L.head.right.right.right = L.push(L.head.right.right.right, 45); + L.head.right.right.right = L.push(L.head.right.right.right, 40); + L.head.right.right.right = L.push(L.head.right.right.right, 35); + + // flatten the list + L.head = L.flatten(L.head); + + L.printList(); + } +} \ No newline at end of file diff --git a/src/main/java/linkedLists/FlattenMultiLevelLinkedList.java b/src/main/java/linkedLists/FlattenMultiLevelLinkedList.java new file mode 100644 index 0000000..3906f0f --- /dev/null +++ b/src/main/java/linkedLists/FlattenMultiLevelLinkedList.java @@ -0,0 +1,79 @@ +package linkedLists; + +/** + * https://leetcode.com/problems/flatten-a-multilevel-doubly-linked-list/discuss/150321/Easy-Understanding-Java-beat-95.7-with-Explanation + */ +class FlattenMultiLevelLinkedList { + + public Node flatten(Node head) { + if (head == null) { + return head; + } + // Pointer + Node p = head; + while (p != null) { + if (p.child == null) { + p = p.next; + continue; + } + /* CASE 2: got child, find the tail of the child and link it to p.next */ + Node temp = p.child; + + while (temp.next != null) { + temp = temp.next; + } + // Connect tail with p.next, if it is not null + temp.next = p.next; + if (p.next != null) { + p.next.prev = temp; + } + // Connect p with p.child, and remove p.child + p.next = p.child; + p.child.prev = p; + p.child = null; + } + return head; + } + + /** + * tricky recursion + * + */ + public Node flattenRecur(Node head) { + if (head == null) return head; + + Node root = head; + while (root != null && root.child == null) { + root = root.next; + } + if (root == null) return head; + + Node transformedList = flattenRecur(root.child); + + Node next = root.next; + + root.next = transformedList; + transformedList.prev = root; + + while (transformedList.next != null) { + transformedList = transformedList.next; + } + transformedList.next = next; + + if (next != null) next.prev = transformedList; + root.child = null; + + return head; + } + + static class Node { + long data; + Node next; + Node prev; + Node child; + + public Node(long id) { + this.data = id; + } + } +} \ No newline at end of file diff --git a/src/main/java/linkedLists/FlattenNestedIterator.java b/src/main/java/linkedLists/FlattenNestedIterator.java new file mode 100644 index 0000000..2569013 --- /dev/null +++ b/src/main/java/linkedLists/FlattenNestedIterator.java @@ -0,0 +1,52 @@ +package linkedLists; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +public class FlattenNestedIterator implements Iterator { + + + List flattenedList; + int index = 0; + + public FlattenNestedIterator(List nestedList) { + flattenedList = new ArrayList<>(); + populateList(nestedList); + } + + @Override + public Integer next() { + Integer temp = flattenedList.get(index); + index++; + return temp; + } + + @Override + public boolean hasNext() { + return flattenedList.size() > index; + } + + private void populateList(List nestedList) { + if (nestedList == null || nestedList.isEmpty()) return; + for (NestedInteger nestedInteger : nestedList) { + populateList(nestedInteger.getList()); + if (nestedInteger.getInteger() != null) flattenedList.add(nestedInteger.getInteger()); + } + } +} + +interface NestedInteger { + + public boolean isInteger(); + + // @return the single integer that this NestedInteger holds, if it holds a single integer + // Return null if this NestedInteger holds a nested list + public Integer getInteger(); + + // @return the nested list that this NestedInteger holds, if it holds a nested list + // Return null if this NestedInteger holds a single integer + public List getList(); +} + + diff --git a/src/main/java/linkedLists/InsertionSortList.java b/src/main/java/linkedLists/InsertionSortList.java new file mode 100644 index 0000000..91f237f --- /dev/null +++ b/src/main/java/linkedLists/InsertionSortList.java @@ -0,0 +1,52 @@ +package linkedLists; + +/** + * tricky linked list sort + * + * Sort a linked list using insertion sort. + * Algorithm of Insertion Sort: + * Insertion sort iterates, + * consuming one input element each repetition, and growing a sorted output list. + * At each iteration, insertion sort removes one element from the input data, finds the location it belongs within the sorted list, and inserts it there. + * It repeats until no input elements remain. + */ +public class InsertionSortList { + public static ListNode insertionSortList(ListNode head) { + ListNode dummy = new ListNode(0); + ListNode curr = head; + + while (curr != null) { + // At each iteration, we insert an element into the resulting list. + ListNode prev = dummy; + + // find the position to insert the current node + while (prev.next != null && prev.next.val < curr.val) { + prev = prev.next; + } + + ListNode next = curr.next; + + // this means given me what ever you have in next so that i(curr) can keep as next items + curr.next = prev.next; + // i(curr) got your next items, let me(curr) attach as your next item. + // this is the insertion part of the algo + prev.next = curr; + + // moving on to the next iteration + curr = next; + } + + return dummy.next; + } + + public static void main(String[] args) { + ListNode node = new ListNode(7); + node.next = new ListNode(3); + node.next.next = new ListNode(2); + node.next.next.next = new ListNode(8); + node.next.next.next.next = new ListNode(9); + node.next.next.next.next.next = new ListNode(1); + + insertionSortList(node); + } +} \ No newline at end of file diff --git a/src/main/java/linkedLists/LinkedListRemoveDuplicates.java b/src/main/java/linkedLists/LinkedListRemoveDuplicates.java new file mode 100644 index 0000000..af31a85 --- /dev/null +++ b/src/main/java/linkedLists/LinkedListRemoveDuplicates.java @@ -0,0 +1,36 @@ +package linkedLists; + +class LinkedListRemoveDuplicate{ + /** + * Method to remove duplicates from Linked list + * when no additional buffer is allowed. + * + * Time Complexity : O(n^2) + * Space Complexity : O(1) + * + * @param head + */ + public static void removeDuplicatesWithoutBuffer(ListNode head) { + /* If head is null, stop processing */ + if (head == null) { + return; + } + /* We will need two pointers here i.e current and runner. + * When current is pointing to a node, move runner through + * rest of the list, checking for duplicates */ + ListNode current = head; + while (current != null) { + /* Have runner point to current node */ + ListNode runner = current; + while (runner.next != null) { + /* If it is duplicate, jump runner over the node */ + if (runner.next.val == current.val) { + runner.next = runner.next.next; + } else { + runner = runner.next; + } + } + current = current.next; + } + } +} \ No newline at end of file diff --git a/src/main/java/linkedLists/LinkedListToBST.java b/src/main/java/linkedLists/LinkedListToBST.java new file mode 100644 index 0000000..63a4974 --- /dev/null +++ b/src/main/java/linkedLists/LinkedListToBST.java @@ -0,0 +1,112 @@ +package linkedLists; + +class LinkedListToBST { + + static LNode head; + + class LNode { + int data; + LNode next, prev; + + LNode(int d) { + data = d; + next = prev = null; + } + } + + class TNode { + int data; + TNode left, right; + + TNode(int d) { + data = d; + left = right = null; + } + } + + TNode sortedListToBST() { + int n = countNodes(head); + return sortedListToBSTRecur(n); + } + + TNode sortedListToBSTRecur(int n) { + if (n <= 0) + return null; + + TNode left = sortedListToBSTRecur(n / 2); + TNode root = new TNode(head.data); + root.left = left; + head = head.next; + root.right = sortedListToBSTRecur(n - n / 2 - 1); + + return root; + } + + int countNodes(LNode head) { + int count = 0; + LNode temp = head; + while (temp != null) { + temp = temp.next; + count++; + } + return count; + } + + /* + * Function to insert a node at the beginging of the Doubly Linked List + */ + void push(int new_data) { + /* allocate node */ + LNode new_node = new LNode(new_data); + + /* + * since we are adding at the begining, prev is always NULL + */ + new_node.prev = null; + + /* link the old list off the new node */ + new_node.next = head; + + /* change prev of head node to new node */ + if (head != null) + head.prev = new_node; + + /* move the head to point to the new node */ + head = new_node; + } + + void printList(LNode node) { + while (node != null) { + System.out.print(node.data + " "); + node = node.next; + } + } + + void preOrder(TNode node) { + if (node == null) + return; + System.out.print(node.data + " "); + preOrder(node.left); + preOrder(node.right); + } + + public static void main(String[] args) { + LinkedListToBST llist = new LinkedListToBST(); + + llist.push(7); + llist.push(6); + llist.push(5); + llist.push(4); + llist.push(3); + llist.push(2); + llist.push(1); + + System.out.println("Given Linked List "); + llist.printList(head); + + TNode root = llist.sortedListToBST(); + System.out.println(""); + System.out.println("Pre-Order Traversal of constructed BST "); + llist.preOrder(root); + } +} \ No newline at end of file diff --git a/src/main/java/linkedLists/Main.java b/src/main/java/linkedLists/Main.java new file mode 100644 index 0000000..a75b6ba --- /dev/null +++ b/src/main/java/linkedLists/Main.java @@ -0,0 +1,86 @@ +package linkedLists; + +import java.util.*; + +class MyStack { + Stack s; + Integer minEle; + + MyStack() { + s = new Stack(); + } + + void getMin() { + if (s.isEmpty()) { + System.out.println("Stack is empty"); + } else { + System.out.println("Minimum Element in the " + " stack is: " + minEle); + } + } + + void peek() { + if (s.isEmpty()) { + System.out.println("Stack is empty "); + return; + } + + Integer t = s.peek(); + System.out.print("Top Most Element is: "); + + if (t < minEle) { + System.out.println(minEle); + } else { + System.out.println(t); + } + } + + void pop() { + if (s.isEmpty()) { + System.out.println("Stack is empty"); + return; + } + + System.out.print("Top Most Element Removed: "); + Integer t = s.pop(); + if (t < minEle) { + System.out.println(minEle); + minEle = 2 * minEle - t; + } else { + System.out.println(t); + } + } + + void push(Integer x) { + if (s.isEmpty()) { + minEle = x; + s.push(x); + System.out.println("Number Inserted: " + x); + return; + } + if (x < minEle) { + s.push(2 * x - minEle); + minEle = x; + } else { + s.push(x); + } + + System.out.println("Number Inserted: " + x); + } +}; + +public class Main { + public static void main(String[] args) { + MyStack s = new MyStack(); + s.push(7); + s.push(5); + s.push(2); + s.pop(); + s.push(4); + s.getMin(); + s.push(1); + s.pop(); + s.getMin(); + s.pop(); + s.peek(); + } +} \ No newline at end of file diff --git a/src/main/java/linkedLists/MergeKSortedLists.java b/src/main/java/linkedLists/MergeKSortedLists.java new file mode 100644 index 0000000..6533d9c --- /dev/null +++ b/src/main/java/linkedLists/MergeKSortedLists.java @@ -0,0 +1,65 @@ +package linkedLists; + +import java.util.PriorityQueue; + +public class MergeKSortedLists { + + public ListNode mergeKLists(ListNode[] lists) { + if (lists.length == 0) return null; + PriorityQueue queue = new PriorityQueue<>((a, b) -> Integer.compare(a.val, b.val)); + + for (ListNode list : lists) { + if (list != null) queue.offer(list); + } + + ListNode dummy = new ListNode(-1); + ListNode head = dummy; + + while (!queue.isEmpty()) { + ListNode temp = queue.poll(); + head.next = temp; + if (temp.next != null) queue.offer(temp.next); + head = head.next; + } + + return dummy.next; + } + + public ListNode mergeKListsEfficient(ListNode[] lists) { + return merge(lists, 0, lists.length - 1); + } + + public ListNode merge(ListNode[] lists, int start, int end) { + if (start == end) return lists[start]; + if (start > end) return null; + int mid = (start + end) / 2; + ListNode left = merge(lists, start, mid); + ListNode right = merge(lists, mid + 1, end); + return mergeTwo(left, right); + } + + public ListNode mergeTwo(ListNode node1, ListNode node2) { + if (node1 == null) return node2; + if (node2 == null) return node1; + + ListNode head = new ListNode(0), tail = head; + while (node1 != null && node2 != null) { + if (node1.val < node2.val) { + tail.next = node1; + node1 = node1.next; + } else { + tail.next = node2; + node2 = node2.next; + } + tail = tail.next; + } + + if (node1 != null) { + tail.next = node1; + } + if (node2 != null) { + tail.next = node2; + } + return head.next; + } +} diff --git a/src/geeksforgeeks/MergeSortLinkedList.java b/src/main/java/linkedLists/MergeSortLinkedList.java similarity index 86% rename from src/geeksforgeeks/MergeSortLinkedList.java rename to src/main/java/linkedLists/MergeSortLinkedList.java index 4e8fbb2..cac69ea 100644 --- a/src/geeksforgeeks/MergeSortLinkedList.java +++ b/src/main/java/linkedLists/MergeSortLinkedList.java @@ -1,38 +1,37 @@ -package geeksforgeeks; +package linkedLists; +/** + * tricky linked list + * revise + * https://www.geeksforgeeks.org/merge-sort-for-linked-list/ + */ public class MergeSortLinkedList { public static ListNode sortList(ListNode head) { - if (head == null || head.next == null) + if (head == null || head.next == null) { return head; - + } // step 1. cut the list to two halves ListNode prev = null; ListNode slow = head; ListNode fast = head; - // find the mid node while (fast != null && fast.next != null) { prev = slow; slow = slow.next; fast = fast.next.next; } - prev.next = null; - // step 2. sort each half ListNode l1 = sortList(head); ListNode l2 = sortList(slow); - // step 3. merge l1 and l2 return merge(l1, l2); } static ListNode merge(ListNode l1, ListNode l2) { - ListNode node = new ListNode(0); ListNode temp = node; - while (l1 != null && l2 != null) { if (l1.val < l2.val) { temp.next = l1; @@ -43,13 +42,12 @@ static ListNode merge(ListNode l1, ListNode l2) { } temp = temp.next; } - - if (l1 != null) + if (l1 != null) { temp.next = l1; - - if (l2 != null) + } + if (l2 != null) { temp.next = l2; - + } return node.next; } @@ -65,15 +63,5 @@ public static void main(String[] args) { listNode = listNode.next; } } - } -class ListNode { - - ListNode next; - int val; - - public ListNode(int val) { - this.val = val; - } -} \ No newline at end of file diff --git a/src/main/java/linkedLists/MergeTwoLinkedList.java b/src/main/java/linkedLists/MergeTwoLinkedList.java new file mode 100644 index 0000000..f5d1342 --- /dev/null +++ b/src/main/java/linkedLists/MergeTwoLinkedList.java @@ -0,0 +1,46 @@ +package linkedLists; + +/** + * https://www.geeksforgeeks.org/merge-two-sorted-linked-lists/ + */ +public class MergeTwoLinkedList { + + public static ListNode mergeTwoLists(ListNode l1, ListNode l2) { + if (l1 == null) { + return l2; + } else if (l2 == null) { + return l1; + } + ListNode dummy = new ListNode(0); + ListNode curr = dummy; + while (l1 != null && l2 != null) { + if (l1.val <= l2.val) { + curr.next = l1; + l1 = l1.next; + } else { + curr.next = l2; + l2 = l2.next; + } + curr = curr.next; + } + curr.next = l1 == null ? l2 : l1; + return dummy.next; + } + + public static void main(String[] args) { + ListNode l1 = new ListNode(1); + l1.next = new ListNode(2); + l1.next.next = new ListNode(3); + l1.next.next.next = new ListNode(4); + + ListNode l2 = new ListNode(1); + // l2.next = new ListNode(7); + + ListNode head = mergeTwoLists(l1, l2); + + while (head != null) { + System.out.println(head.val); + head = head.next; + } + } +} diff --git a/src/main/java/linkedLists/MergeTwoLinkedLists.java b/src/main/java/linkedLists/MergeTwoLinkedLists.java new file mode 100644 index 0000000..b116d47 --- /dev/null +++ b/src/main/java/linkedLists/MergeTwoLinkedLists.java @@ -0,0 +1,111 @@ +package linkedLists; + +public class MergeTwoLinkedLists { + + public static Node head_1; + public static Node head_2; + public static Node head; + + public static void main(String[] args) { + addNodeInList1(); + addNodeInList2(); + printLst(); + mergeLists(); + printList(); + } + + private static void printList() { + Node temp = head; + + while (temp != null) { + System.out.println(temp.getData()); + temp = temp.getNode(); + } + + } + + private static void mergeLists() { + Node temp_1 = head_1; + Node temp_2 = head_2; + Node temp = head; + + while (null != temp_1 && temp_2 != null) { + if (temp_1.getData() < temp_2.getData()) { + temp = insertNode(temp_1.getData(), temp); + temp_1 = temp_1.getNode(); + } else { + temp = insertNode(temp_2.getData(), temp); + temp_2 = temp_2.getNode(); + } + } + while (temp_1 != null && temp_2 == null) { + Node node = new Node(temp_1.getData()); + temp.setNode(node); + temp = temp.getNode(); + temp_1 = temp_1.getNode(); + } + while (temp_2 != null && temp_1 == null) { + Node node = new Node(temp_2.getData()); + temp.setNode(node); + temp = temp.getNode(); + temp_2 = temp_2.getNode(); + } + } + + private static Node insertNode(int data, Node temp) { + Node node = new Node(data); + if (temp == null) { + head = node; + temp = node; + } else { + temp.setNode(node); + temp = temp.getNode(); + } + return temp; + } + + private static void printLst() { + + Node temp_1 = head_1; + while (temp_1 != null) { + System.out.print(temp_1.getData() + " "); + temp_1 = temp_1.getNode(); + } + + System.out.println(); + + Node temp = head_2; + while (temp != null) { + System.out.print(temp.getData() + " "); + temp = temp.getNode(); + } + System.out.println(); + System.out.println(); + } + + private static void addNodeInList2() { + int n = 5; + Node node = new Node(2); + head_1 = node; + Node temp = head_1; + for (int i = 0; i < 5; i++) { + Node newNode = new Node(n); + temp.setNode(newNode); + temp = newNode; + n += 2; + } + } + + private static void addNodeInList1() { + int n = 2; + Node node = new Node(1); + head_2 = node; + Node temp = head_2; + for (int i = 0; i < 5; i++) { + Node newNode = new Node(n); + temp.setNode(newNode); + temp = newNode; + n += 3; + } + } +} diff --git a/src/main/java/linkedLists/MiddleElement.java b/src/main/java/linkedLists/MiddleElement.java new file mode 100644 index 0000000..b441c62 --- /dev/null +++ b/src/main/java/linkedLists/MiddleElement.java @@ -0,0 +1,28 @@ +package linkedLists; + +public class MiddleElement { + + static SinglyLinkedListNode head; + + public static void main(String[] args) { + head = new SinglyLinkedListNode(1); + head.next = new SinglyLinkedListNode(2); + head.next.next = new SinglyLinkedListNode(3); + head.next.next.next = new SinglyLinkedListNode(4); + head.next.next.next.next = new SinglyLinkedListNode(5); + + findMiddleElement(); + } + + private static void findMiddleElement() { + SinglyLinkedListNode temp = head; + SinglyLinkedListNode slowPointer = temp; + SinglyLinkedListNode fastPointer = temp; + while (fastPointer != null && fastPointer.next != null) { + slowPointer = slowPointer.next; + fastPointer = fastPointer.next.next; + } + System.out.println(slowPointer.data); + } + +} diff --git a/src/main/java/linkedLists/NextLargestList.java b/src/main/java/linkedLists/NextLargestList.java new file mode 100644 index 0000000..8b2c300 --- /dev/null +++ b/src/main/java/linkedLists/NextLargestList.java @@ -0,0 +1,58 @@ +package linkedLists; + +import java.util.Stack; + +/** + * https://leetcode.com/problems/next-greater-node-in-linked-list/ + */ +class NextLargestList { + + public int[] nextLargerNodes(ListNode head) { + + if (head == null) { + return new int[0]; + } + int size = getSize(head); + int[] result = new int[size]; + head = reverse(head); + Stack stack = new Stack<>(); + stack.push(head.val); + head = head.next; + int i = size - 2; + result[size - 1] = 0; + while (head != null) { + + while (!stack.isEmpty() && stack.peek() <= head.val) { + stack.pop(); + } + result[i] = stack.isEmpty() ? 0 : stack.peek(); + stack.push(head.val); + head = head.next; + i--; + } + + return result; + } + + public int getSize(ListNode head) { + int size = 0; + ListNode root = head; + while (root != null) { + root = root.next; + size++; + } + return size; + } + + public ListNode reverse(ListNode head) { + ListNode root = head; + ListNode prev = null; + while (root != null) { + ListNode next = root.next; + root.next = prev; + prev = root; + root = next; + } + return prev; + } +} \ No newline at end of file diff --git a/src/main/java/linkedLists/Node.java b/src/main/java/linkedLists/Node.java new file mode 100644 index 0000000..31f74fe --- /dev/null +++ b/src/main/java/linkedLists/Node.java @@ -0,0 +1,54 @@ +package linkedLists; + +public class Node { + + public Node left; + public Node right; + public Node next; + public int data; + public Node child; + public Node prev; + + public Node(int data) { + this.data = data; + } + + public Node getLeft() { + return left; + } + + public void setLeft(Node left) { + this.left = left; + } + + public Node getRight() { + return right; + } + + public void setRight(Node right) { + this.right = right; + } + + public Node getNext() { + return next; + } + + public void setNext(Node next) { + this.next = next; + } + + public int getData() { + return data; + } + + public void setData(int data) { + this.data = data; + } + + public void setNode(Object o) { + } + + public Node getNode() { + return this; + } +} diff --git a/src/main/java/linkedLists/PalindromeSinglyLinkedList.java b/src/main/java/linkedLists/PalindromeSinglyLinkedList.java new file mode 100644 index 0000000..1095a6d --- /dev/null +++ b/src/main/java/linkedLists/PalindromeSinglyLinkedList.java @@ -0,0 +1,53 @@ +package linkedLists; + +import java.util.Stack; + +public class PalindromeSinglyLinkedList { + + class Node { + int data; + Node next; + + public Node(int data) { + this.data = data; + } + + } + + public static void main(String[] args) { + + PalindromeSinglyLinkedList palindrome = new PalindromeSinglyLinkedList(); + Node head = palindrome.new Node(1); + head.next = palindrome.new Node(2); + head.next.next = palindrome.new Node(3); + head.next.next.next = palindrome.new Node(2); + head.next.next.next.next = palindrome.new Node(1); + + System.out.println(palindrome.isPalindrome(head)); + + } + + private boolean isPalindrome(Node head) { + Node slow = head; + Node fast = head; + + Stack stack = new Stack(); + + while (fast != null && fast.next != null) { + stack.push(slow); + slow = slow.next; + fast = fast.next.next; + } + + while (!stack.isEmpty()) { + Node pop = stack.pop(); + if (pop.data != slow.next.data) { + return false; + } else { + slow = slow.next; + } + } + return true; + } + +} diff --git a/src/main/java/linkedLists/RandomListNode.java b/src/main/java/linkedLists/RandomListNode.java new file mode 100644 index 0000000..63e1346 --- /dev/null +++ b/src/main/java/linkedLists/RandomListNode.java @@ -0,0 +1,35 @@ +package linkedLists; + +public class RandomListNode { + RandomListNode next; + RandomListNode random; + double data; + + public RandomListNode(double d) { + data = d; + } + + public RandomListNode getNext() { + return next; + } + + public void setNext(RandomListNode next) { + this.next = next; + } + + public RandomListNode getRandom() { + return random; + } + + public void setRandom(RandomListNode random) { + this.random = random; + } + + public double getData() { + return data; + } + + public void setData(double data) { + this.data = data; + } +} diff --git a/src/main/java/linkedLists/RemoveDuplicates.java b/src/main/java/linkedLists/RemoveDuplicates.java new file mode 100644 index 0000000..1e3d892 --- /dev/null +++ b/src/main/java/linkedLists/RemoveDuplicates.java @@ -0,0 +1,80 @@ +package linkedLists; + +import java.util.HashMap; +import java.util.Map; + +/** + * https://leetcode.com/problems/remove-duplicates-from-an-unsorted-linked-list + */ +public class RemoveDuplicates { + + public ListNode deleteDuplicatesFromSorted(ListNode head) { + ListNode list = head; + + while(list != null) { + if (list.next == null) { + break; + } + if (list.val == list.next.val) { + list.next = list.next.next; + } else { + list = list.next; + } + } + + return head; + } + + public ListNode deleteDuplicatesFromSortedII(ListNode head) { + // sentinel + ListNode sentinel = new ListNode(0); + sentinel.next = head; + // predecessor = the last node before the sublist of duplicates + // this will be always one node behind + ListNode pred = sentinel; + + while (head != null) { + // if it's a beginning of duplicates sublist + // skip all duplicates + if (head.next != null && head.val == head.next.val) { + // move till the end of duplicates sublist + while (head.next != null && head.val == head.next.val) { + head = head.next; + } + // skip all duplicates + pred.next = head.next; + // otherwise, move predecessor + } else { + pred = pred.next; + } + + // move forward + head = head.next; + } + return sentinel.next; + } + + public ListNode deleteDuplicatesUnsorted(ListNode head) { + // Key: val Value: its frequency + Map map = new HashMap<>(); + ListNode curr = head; + while (curr != null) { + map.put(curr.val, map.getOrDefault(curr.val, 0) + 1); + curr = curr.next; + } + + ListNode dummy = new ListNode(0); + curr = dummy; + while (head != null) { + if (map.get(head.val) == 1) { + curr.next = head; + curr = curr.next; + } + + head = head.next; + } + + curr.next = null; + return dummy.next; + } +} diff --git a/src/main/java/linkedLists/ReverseKBlockNode.java b/src/main/java/linkedLists/ReverseKBlockNode.java new file mode 100644 index 0000000..812ce56 --- /dev/null +++ b/src/main/java/linkedLists/ReverseKBlockNode.java @@ -0,0 +1,117 @@ +package linkedLists; + +/** + * https://leetcode.com/problems/reverse-nodes-in-k-group/ + * + * tricky + */ +public class ReverseKBlockNode { + + static ListNode head; + static int k = 3; + + public static void main(String[] args) { + 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); + head.next.next.next.next.next = new ListNode(6); + head.next.next.next.next.next.next = new ListNode(7); + head.next.next.next.next.next.next.next = new ListNode(8); + head.next.next.next.next.next.next.next.next = new ListNode(9); + head.next.next.next.next.next.next.next.next.next = new ListNode(10); + + reverseKNodes(head, 2); + print(head); + } + + + private static void print(ListNode current) { + ListNode node = current; + while (node != null) { + System.out.println(node.val); + node = node.next; + } + } + + public static ListNode reverseKNodes(ListNode head, int k) { + if (head == null) return null; + ListNode root = head; + int count = 0; + //1. test weather we have more then k node left, if less then k node left we just return head + while (count < k) { // && root!=null add this condition to reverse remaining elements + //base case: head listnode contains less than k nodes, + // in this case, return the original listnode(aka head) + if (root == null) return head; + root = root.next; + count++; + } + // 2.reverse k node at current level + ListNode prev = reverseKNodes(root, k); //prev node point to the answer of sub-problem + while (count > 0) { + ListNode next = head.next; + head.next = prev; + prev = head; + head = next; + count--; + + } + + return prev; + } + + /** + * Reverse a link list between begin and end exclusively + * an example: + * a linked list: + * 0->1->2->3->4->5->6 + * | | + * begin end + * after call begin = reverse(begin, end) + * + * 0->3->2->1->4->5->6 + * | | + * begin end + * @return the reversed list's 'begin' node, which is the precedence of node end + */ + + public ListNode reverseKGroup(ListNode head, int k) { + ListNode begin; + if (head == null || head.next == null || k == 1) + return head; + + ListNode dummyHead = new ListNode(-1); + dummyHead.next = head; + begin = dummyHead; + int i = 0; + + while (head != null) { + i++; + ListNode next = head.next; + if (i % k == 0) { + begin = reverse(begin, next); + } + head = next; + + } + return dummyHead.next; + + } + + public ListNode reverse(ListNode begin, ListNode end) { + ListNode curr = begin.next; + ListNode prev = begin; + ListNode first = curr; + while (curr != end) { + ListNode next = curr.next; + curr.next = prev; + prev = curr; + curr = next; + } + begin.next = prev; + first.next = curr; + return first; + } + +} diff --git a/src/main/java/linkedLists/ReverseLinkedList.java b/src/main/java/linkedLists/ReverseLinkedList.java new file mode 100644 index 0000000..4797a12 --- /dev/null +++ b/src/main/java/linkedLists/ReverseLinkedList.java @@ -0,0 +1,41 @@ +package linkedLists; + +public class ReverseLinkedList { + + public ListNode reverseList(ListNode head) { + if(head==null || head.next==null) return head; + // for 1->2->3->4->5->null + // 5 will be returned(5->null) when head is at 4 + // head.next.next means 4's next(5)'s next is 4=> 5->4->null for top recursion + // when head is 3, 5->4->null is returned as tmp at this point 3's->4 and 5's-> also 4 + // head.next.next means 3's next 4's(5->4->null) next is 3=> 5->4->3->null + ListNode temp= reverseList(head.next); + head.next.next= head; + head.next=null; + return temp; + + } + + public ListNode reverseList1(ListNode head) { + if(head==null) return null; + ListNode root=head; + ListNode prev=null; + while(root!=null){ + ListNode temp=root.next; + root.next=prev; + prev=root; + root=temp; + } + return prev; + + } + + public static void main(String[] args) { + ListNode node= new ListNode(1); + node.next= new ListNode(2); + node.next.next= new ListNode(3); + node.next.next.next= new ListNode(4); + node.next.next.next.next= new ListNode(5); + new ReverseLinkedList().reverseList(node); + } +} diff --git a/src/main/java/linkedLists/ReverseLinkedListBetweenMandN.java b/src/main/java/linkedLists/ReverseLinkedListBetweenMandN.java new file mode 100644 index 0000000..7282cb1 --- /dev/null +++ b/src/main/java/linkedLists/ReverseLinkedListBetweenMandN.java @@ -0,0 +1,36 @@ +package linkedLists; + + +public class ReverseLinkedListBetweenMandN { + + public ListNode reverseBetween(ListNode head, int m, int n) { + ListNode fakeHead = new ListNode(-1); + fakeHead.next = head; + ListNode prev = fakeHead; + ListNode curr = fakeHead.next; + int i = 1; + while (i < m) { // i travels till m-1 to store prev + prev = curr; + curr = curr.next; + i++; + } + ListNode node = prev; + while (i <= n) { // normal reverse linkedlist + ListNode tmp = curr.next; + curr.next = prev; + prev = curr; + curr = tmp; + i++; + } + + // for 1->2->3->4->5->null, m = 2, n = 4, + // after the second while, we got 1<->2<-3<-4 5->null + // now cur = 5, pre = 4, node = 1, + // so node.next = 2, node.next.next = cur means 2->5 + // node.next = pre means 1->4, then we get the result 1->4->3->2->5->null. + + node.next.next = curr; + node.next = prev; + return fakeHead.next; + } +} diff --git a/src/main/java/linkedLists/ReversePairsNode.java b/src/main/java/linkedLists/ReversePairsNode.java new file mode 100644 index 0000000..48af789 --- /dev/null +++ b/src/main/java/linkedLists/ReversePairsNode.java @@ -0,0 +1,54 @@ +package linkedLists; + +public class ReversePairsNode { + public ListNode reversePairs(ListNode head) { + if (head == null) return head; + + ListNode dummy = new ListNode(-1); + dummy.next = head; + ListNode current = dummy; + + + // dummy->1->2->3->4->null + //explanation for one loop rest are same. + while (current.next != null && current.next.next != null) { + // current points to dummy in the beginning. + // first -> 1 + ListNode first = current.next; + //second -> 2 + ListNode second = current.next.next; + // temp-> 3 + ListNode third = second.next; + + // dummy->2 + current.next = second; + + //1->3 + first.next = third; + //2->1 + second.next = first; + // curr=1 + current = first; + // now dummy->2->1->3->4 + } + + return dummy.next; + } + // 1->2->3->4->5->null + public Node reversePairsRec(Node head) { + + if (head == null || head.next == null) { + return head; + } + + Node next = head.next; // next= 2 + Node temp = head.next.next; // temp= 3 + + next.next = head; // 2->1 + head = next;// head=2->1 + + head.next.next = reversePairsRec(temp); // the rest will be taken care by recursion + return head; + } + +} diff --git a/src/main/java/linkedLists/RotateList.java b/src/main/java/linkedLists/RotateList.java new file mode 100644 index 0000000..70cf43b --- /dev/null +++ b/src/main/java/linkedLists/RotateList.java @@ -0,0 +1,29 @@ +package linkedLists; + + +public class RotateList { + public ListNode rotateRight(ListNode head, int k) { + if(k==0 || head==null) return head; + + ListNode tempHead= head; + int length=1; + while(tempHead.next!=null){ + length++; + tempHead=tempHead.next; + } + tempHead.next=head; + k%=length; + for(int i=0;i> observers; + + public Publisher() { + this.observers = new HashMap<>(); + this.observers.put(ObserverType.SELLER, new LinkedList<>()); + this.observers.put(ObserverType.BUYER, new LinkedList<>()); + } + + public Map> getObservers() { + return this.observers; + } +} diff --git a/src/main/java/lld/auctionSystem/models/Seller.java b/src/main/java/lld/auctionSystem/models/Seller.java new file mode 100644 index 0000000..582ba68 --- /dev/null +++ b/src/main/java/lld/auctionSystem/models/Seller.java @@ -0,0 +1,16 @@ +package lld.auctionSystem.models; + + +import java.util.List; + +public class Seller extends User { + private List auctions; //List of AuctionId + + public Seller( String sellerId, String username, String emailId, String phoneNo) { + super(sellerId, username,emailId, phoneNo); + } + + public List getAuctions() { + return auctions; + } +} diff --git a/src/main/java/lld/auctionSystem/models/User.java b/src/main/java/lld/auctionSystem/models/User.java new file mode 100644 index 0000000..2d702e5 --- /dev/null +++ b/src/main/java/lld/auctionSystem/models/User.java @@ -0,0 +1,70 @@ +package lld.auctionSystem.models; + + +import java.util.LinkedList; +import java.util.List; + +public class User extends Observer { + private String userId; + private String username; + private String emailId; + private String phoneNo; + private List notifications; //Always insert latest data at the head of the list. + // TODO - private List bankAccountDetils; + // TODO - private List

addresses; + + public User( String userId, String username, String emailId, String phoneNo) { + this.userId = userId; + this.username = username; + this.emailId = emailId; + this.phoneNo = phoneNo; + this.notifications = new LinkedList<>(); + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getEmailId() { + return emailId; + } + + public void setEmailId(String emailId) { + this.emailId = emailId; + } + + public String getPhoneNo() { + return phoneNo; + } + + public void setPhoneNo(String phoneNo) { + this.phoneNo = phoneNo; + } + + public List getNotifications() { + return notifications; + } + + @Override + public boolean equals(Object obj) { + if(obj==null) { + return false; + } + if(obj instanceof User) { + return this.username.equals(((User) obj).getUsername()); + } + return false; + } +} \ No newline at end of file diff --git a/src/main/java/lld/auctionSystem/repository/AuctionRepository.java b/src/main/java/lld/auctionSystem/repository/AuctionRepository.java new file mode 100644 index 0000000..8fe432f --- /dev/null +++ b/src/main/java/lld/auctionSystem/repository/AuctionRepository.java @@ -0,0 +1,34 @@ +package lld.auctionSystem.repository; + + +import lld.auctionSystem.models.Auction; + +import java.util.HashMap; +import java.util.Map; + +public class AuctionRepository { + public Map auctions; + + // TODO - Implement thread safe lazy initialization singleton pattern. + private AuctionRepository() { + this.auctions = new HashMap<>(); + } + private static AuctionRepository single_instance = null; + public static AuctionRepository getInstance() { + if(single_instance==null) { + single_instance = new AuctionRepository(); + } + return single_instance; + } + + public void addDataToRepository(String key, Auction value) { + this.auctions.put(key, value); + } + + public Auction getData(String key) { + if(key==null) { + return null; + } + return this.auctions.get(key); + } +} diff --git a/src/main/java/lld/auctionSystem/repository/BuyerRepository.java b/src/main/java/lld/auctionSystem/repository/BuyerRepository.java new file mode 100644 index 0000000..99c5c00 --- /dev/null +++ b/src/main/java/lld/auctionSystem/repository/BuyerRepository.java @@ -0,0 +1,36 @@ +package lld.auctionSystem.repository; + + +import lld.auctionSystem.models.Buyer; + +import java.util.HashMap; +import java.util.Map; + +public class BuyerRepository { + + public static Map Buyers = new HashMap<>(); + + // TODO - Implement thread safe lazy initialization singleton pattern. + private BuyerRepository() { + this.Buyers = new HashMap<>(); + } + private static BuyerRepository single_instance = null; + public static BuyerRepository getInstance() { + if(single_instance==null) { + single_instance = new BuyerRepository(); + } + return single_instance; + } + + public void addDataToRepository(String key, Buyer value) { + this.Buyers.put(key, value); + } + + public Buyer getData(String key) { + if(key==null) { + return null; + } + return this.Buyers.get(key); + } + +} diff --git a/src/main/java/lld/auctionSystem/repository/ProductRepository.java b/src/main/java/lld/auctionSystem/repository/ProductRepository.java new file mode 100644 index 0000000..ffe49b0 --- /dev/null +++ b/src/main/java/lld/auctionSystem/repository/ProductRepository.java @@ -0,0 +1,34 @@ +package lld.auctionSystem.repository; + + +import lld.auctionSystem.models.Product; + +import java.util.HashMap; +import java.util.Map; + +public class ProductRepository { + public static Map products; + + // TODO - Implement thread safe lazy initialization singleton pattern. + private ProductRepository() { + this.products = new HashMap<>(); + } + private static ProductRepository single_instance = null; + public static ProductRepository getInstance() { + if(single_instance==null) { + single_instance = new ProductRepository(); + } + return single_instance; + } + + public void addDataToRepository(String key, Product value) { + this.products.put(key, value); + } + + public Product getData(String key) { + if(key==null) { + return null; + } + return this.products.get(key); + } +} diff --git a/src/main/java/lld/auctionSystem/repository/SellerRepository.java b/src/main/java/lld/auctionSystem/repository/SellerRepository.java new file mode 100644 index 0000000..1bc666f --- /dev/null +++ b/src/main/java/lld/auctionSystem/repository/SellerRepository.java @@ -0,0 +1,38 @@ +package lld.auctionSystem.repository; + + +import lld.auctionSystem.models.Seller; + +import java.util.HashMap; +import java.util.Map; + +public class SellerRepository { + + public Map sellers; + + // TODO - Implement thread safe lazy initialization singleton pattern. + private SellerRepository() { + this.sellers = new HashMap<>(); + } + + private static SellerRepository single_instance = null; + + public static SellerRepository getInstance() { + if (single_instance == null) { + single_instance = new SellerRepository(); + } + return single_instance; + } + + public void addDataToRepository(String key, Seller value) { + this.sellers.put(key, value); + } + + public Seller getData(String key) { + if (key == null) { + return null; + } + return this.sellers.get(key); + } + +} diff --git a/src/main/java/lld/auctionSystem/services/AuctionService.java b/src/main/java/lld/auctionSystem/services/AuctionService.java new file mode 100644 index 0000000..8b748d8 --- /dev/null +++ b/src/main/java/lld/auctionSystem/services/AuctionService.java @@ -0,0 +1,342 @@ +package lld.auctionSystem.services; + + +import lld.auctionSystem.enums.AuctionProductState; +import lld.auctionSystem.enums.AuctionState; +import lld.auctionSystem.enums.ObserverType; +import lld.auctionSystem.enums.PublisherEventType; +import lld.auctionSystem.models.Auction; +import lld.auctionSystem.models.AuctionProduct; +import lld.auctionSystem.models.Event; +import lld.auctionSystem.models.Product; +import lld.auctionSystem.models.Publisher; +import lld.auctionSystem.repository.AuctionRepository; +import lld.auctionSystem.repository.ProductRepository; +import lld.auctionSystem.services.strategies.AuctionDetails; +import lld.auctionSystem.services.strategies.BuyerViewAuctionDetails; +import lld.auctionSystem.services.strategies.SellerViewAuctionDetails; + +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.UUID; + +public class AuctionService implements PublisherService { + private final ProductRepository productRepository = ProductRepository.getInstance(); + private final AuctionRepository auctionRepository = AuctionRepository.getInstance(); + private final BuyerService buyerService = new BuyerService(); + private final SellerService sellerService = new SellerService(); + + private String generateAuctionId() { + return UUID.randomUUID().toString() + ".AI"; + } + + private String generateProductId() { + return UUID.randomUUID().toString() + ".PI"; + } + + private boolean validateSeller(String sellerId) { + return sellerService.validateSeller(sellerId); + } + + private boolean validateBuyer(String buyerId) { + return buyerService.validateBuyer(buyerId); + } + + public void draftAuction(String sellerId) { + boolean validate = this.validateSeller(sellerId); + if (!validate) { + System.out.println("Seller Id is invalid"); + return; + } + Auction auction = new Auction(this.generateAuctionId()); + auctionRepository.addDataToRepository(auction.getAuctionId(), auction); + System.out.println("Auction created. Auction is in DRAFT state."); + } + + public void updateAuctionProduct(String sellerId, String auctionId, String productName, String productDescription, double basePrice) { + boolean validate = this.validateSeller(sellerId); + if (!validate) { + System.out.println("Seller Id is invalid"); + return; + } + validate = this.validateAuction(auctionId); + if (!validate) { + System.out.println("Auction Id is invalid"); + return; + } + Auction auction = auctionRepository.getData(auctionId); + if (auction.getAuctionState() != AuctionState.DRAFTED) { + System.out.println("Auction is not in draft state. Making any change is not allowed."); + return; + } + Product product = new Product(this.generateProductId(), productName, productDescription, auctionId); + AuctionProduct auctionProduct = new AuctionProduct(product.getProductId(), basePrice); + auction.setProducts(Arrays.asList(new AuctionProduct[]{auctionProduct})); + System.out.println("Product Added"); + } + + // TODO - Add functions to update auction details, product details & auction base price. + + private final long MAX_START_TIME_DELAY = 3 * 3600; + private final long MIN_AUCTION_LIFE = 3600; + private final long MAX_AUCTION_LIFE = 3 * 3600; + + public void publishAuction(String sellerId, String auctionId, Date startTime, Date endTime) { + boolean validate = this.validateSeller(sellerId); + if (!validate) { + System.out.println("Seller Id is invalid"); + return; + } + validate = this.validateAuction(auctionId); + if (!validate) { + System.out.println("Auction Id is invalid"); + return; + } + Auction auction = auctionRepository.getData(auctionId); + if (auction.getProducts() == null || auction.getProducts().size() == 0) { + System.out.println("Add a product before publishing auction."); + return; + } + if (auction.getAuctionState() != AuctionState.DRAFTED) { + System.out.println("Auction is not in draft state. Make changes is not allowed."); + return; + } + Date currentTime = new Date(); + if (startTime == null) { + startTime = currentTime; + } + if (endTime == null) { + endTime = new Date(startTime.getTime() + MIN_AUCTION_LIFE); + } + if (endTime.compareTo(currentTime) < 0 || startTime.compareTo(currentTime) < 0) { + System.out.println("Invalid start date or end date. Start Date and End Date should be greater than current Date"); + return; + } else if (startTime.getTime() - currentTime.getTime() > MAX_START_TIME_DELAY) { + System.out.println("Start Date should be less than current Date + 3 hours"); + return; + } else if (endTime.getTime() - startTime.getTime() > MAX_AUCTION_LIFE) { + System.out.println("Auction cannot be organised for more than allowed time."); + return; + } else if (endTime.getTime() - startTime.getTime() < MIN_AUCTION_LIFE) { + System.out.println("Auction cannot be organised for less than threshold time."); + return; + } + auction.setAuctionState(AuctionState.PUBLISHED); + if (currentTime == startTime) { + startAuction(auctionId); + } else { + //TODO - Write logic + /* + Schedule auction in scheduler service. + Schedule the start and end of auction. For that we can have a Scheduler Service. + Scheduler Service will call the startAuction() api of AuctionService to start the auction when is it time. + Scheduler Service will call the endAuction() api of AuctionService to end the auction when it is time. + */ + } + + } + + //Will be called by the Scheduler Service. + public void startAuction(String auctionId) { + boolean validate = this.validateAuction(auctionId); + if (!validate) { + System.out.println("Auction Id is invalid"); + return; + } + Auction auction = auctionRepository.getData(auctionId); + auction.setAuctionState(AuctionState.STARTED); + updateObserversAboutAuctionStarted(auction); + } + + //Will be called by the Scheduler Service. + public void endAuction(String auctionId) { + boolean validate = this.validateAuction(auctionId); + if (!validate) { + System.out.println("Auction Id is invalid"); + return; + } + Auction auction = auctionRepository.getData(auctionId); + for (AuctionProduct auctionProduct : auction.getProducts()) { + if (auctionProduct.getAuctionProductState().equals(AuctionProductState.UNBID)) { + auctionProduct.setAuctionProductState(AuctionProductState.FAILED); + auction.setAuctionState(AuctionState.END_FAILED); + } else if (auctionProduct.getCurrentBid() >= auctionProduct.getBasePrice()) { + auctionProduct.setAuctionProductState(AuctionProductState.SOLD); + } else { + auctionProduct.setAuctionProductState(AuctionProductState.FAILED); + auction.setAuctionState(AuctionState.END_FAILED); + } + } + if (!auction.getAuctionState().equals(AuctionState.END_FAILED)) { + auction.setAuctionState(AuctionState.END_SUCCESS); + } + updateObserversAboutAuctionEnded(auction); + } + + public void placeBid(String auctionId, String productId, String buyerId, double bidValue) { + boolean validate = this.validateAuction(auctionId); + if (!validate) { + System.out.println("Auction Id is invalid"); + return; + } + validate = this.validateBuyer(buyerId); + if (!validate) { + System.out.println("Buyer Id is invalid"); + return; + } + Auction auction = auctionRepository.getData(auctionId); + AuctionProduct auctionProduct = auction.getProducts().stream().filter(ap -> ap.getProductId().equals(productId)).findFirst().orElse(null); + if (auctionProduct == null) { + System.out.println("productId given does not belong to this auction."); + return; + } + if (auction.getAuctionState() == AuctionState.STARTED && + auctionProduct.getAuctionProductState() != AuctionProductState.SOLD && + auctionProduct.getAuctionProductState() != AuctionProductState.FAILED) { + if (bidValue < auctionProduct.getCurrentBid()) { + System.out.println("Bid Value should be greater than current bid price."); + return; + } + if (auctionProduct.getAuctionProductState() == AuctionProductState.UNBID) { + auctionProduct.setAuctionProductState(AuctionProductState.UNSOLD); + } + buyerService.placeBid(buyerId, auctionId, productId, bidValue); + auctionProduct.setCurrentBid(bidValue); + auctionProduct.setCurrentWinningBuyerId(buyerId); + auctionProduct.setAuctionProductState(AuctionProductState.UNSOLD); + updateObserverAboutNewBidPrice(auction, bidValue); + } else { + System.out.println("Either auction is not started or auction is ended or product is sold."); + } + } + + @Override + // Will be called by the ObserverService only. + public boolean subscribe(String auctionId, String observerId, ObserverType type) { + boolean validate = this.validateAuction(auctionId); + if (!validate) { + System.out.println("Auction Id is invalid"); + return false; + } + Auction auction = auctionRepository.getData(auctionId); + auction.getObservers().get(type).add(observerId); + return true; + } + + @Override + //Will be called by the ObserverService only. + public boolean unsubscribe(String auctionId, String observerId, ObserverType type) { + boolean validate = this.validateAuction(auctionId); + if (!validate) { + System.out.println("Auction Id is invalid"); + return false; + } + if (ObserverType.SELLER == type) { + System.out.println("Seller not allowed to unsubscribe his auction"); + return false; + } + Auction auction = auctionRepository.getData(auctionId); + //If observer is a Buyer, check if current observers is not the current winning Buyer + if (ObserverType.BUYER == type) { + boolean eligible = eligibleToUnsubscribe(auction, observerId); + if (!eligible) { + return false; + } + } + auction.getObservers().get(type).remove(observerId); + return true; + } + + private boolean eligibleToUnsubscribe(Auction auction, String observerId) { + List auctionProductList = auction.getProducts(); + for (AuctionProduct auctionProduct : auctionProductList) { + if (auctionProduct.getCurrentWinningBuyerId().equals(observerId)) { + System.out.println("Buyer is current winning bidder for one of the product and so not allowed to unsubscribe."); + return false; + } + } + return true; + } + + private void updateObserversAboutAuctionStarted(Auction auction) { + Event event = new Event(); + event.setEventType(PublisherEventType.AUCTION_STARTED); + event.setMessage("Auction Started! - " + auction.getAuctionId()); + event.setDateTime(new Date()); + + updateObservers(auction, ObserverType.BOTH, event); + } + + private void updateObserversAboutAuctionEnded(Auction auction) { + Event event; + + event = new Event(); + event.setEventType(PublisherEventType.AUCTION_ENDED); + event.setMessage("Auction Ended! - " + auction.getAuctionId()); + event.setDateTime(new Date()); + + updateObservers(auction, ObserverType.BOTH, event); + + for (AuctionProduct auctionProduct : auction.getProducts()) { + if (auctionProduct.getAuctionProductState().equals(AuctionProductState.FAILED)) { + continue; + } + event = new Event(); + event.setEventType(PublisherEventType.AUCTION_ENDED); + event.setMessage("Auction Ended! - " + auction.getAuctionId() + + "Congrats! You bought the product - " + auctionProduct.getProductId()); + event.setDateTime(new Date()); + updateObservers(Arrays.asList(auctionProduct.getCurrentWinningBuyerId()), ObserverType.BUYER, event); + } + } + + private void updateObserverAboutNewBidPrice(Auction auction, double bidValue) { + Event event = new Event(); + event.setEventType(PublisherEventType.BID_VALUE_UPDATED); + event.setMessage("Bid Price updated! Auction - " + auction.getAuctionId() + "New Bid price is: " + bidValue); + event.setDateTime(new Date()); + + updateObservers(auction, ObserverType.BUYER, event); + } + + private void updateObservers(Publisher auction, ObserverType observerType, Event event) { + this.updateObservers(auction.getObservers().get(observerType), observerType, event); + } + + @Override + //Will be called by AuctionSerice class only when the state of auction changes. + public void updateObservers(List observerIds, ObserverType observerType, Event event) { + ObserverService observerService; + switch (observerType) { + case BUYER: + buyerService.updateObserver(observerIds, event); + break; + case SELLER: + sellerService.updateObserver(observerIds, event); + break; + case BOTH: + buyerService.updateObserver(observerIds, event); + sellerService.updateObserver(observerIds, event); + break; + default: + System.out.println("Observer Type not listed"); + } + } + + public String getAuctionDetails(String userId, String auctionId, String algorithm) { + AuctionDetails auctionDetailsObject = null; + if (algorithm.equals("SellerView")) { + auctionDetailsObject = new SellerViewAuctionDetails(); + } else { + auctionDetailsObject = new BuyerViewAuctionDetails(); + } + // Using Strategy Pattern + String response = auctionDetailsObject.getAuctionDetails(userId, auctionId); + return response; + } + + public boolean validateAuction(String auctionId) { + return auctionRepository.getData(auctionId) != null ? true : false; + } +} diff --git a/src/main/java/lld/auctionSystem/services/BuyerService.java b/src/main/java/lld/auctionSystem/services/BuyerService.java new file mode 100644 index 0000000..d1c297a --- /dev/null +++ b/src/main/java/lld/auctionSystem/services/BuyerService.java @@ -0,0 +1,164 @@ +package lld.auctionSystem.services; + + + +import lld.auctionSystem.enums.ObserverType; +import lld.auctionSystem.models.Buyer; +import lld.auctionSystem.models.Event; +import lld.auctionSystem.models.Notification; +import lld.auctionSystem.repository.BuyerRepository; +import lld.auctionSystem.utils.Utils; + +import java.util.*; + +public class BuyerService implements ObserverService { + private final BuyerRepository buyerRepository = BuyerRepository.getInstance(); + private final AuctionService auctionService = new AuctionService(); + private static final ObserverType BUYER_AS_OBSERVER = ObserverType.BUYER; + + private String generateBuyerId() { + return UUID.randomUUID().toString()+".BI"; + } + + public void addBuyer( String username, String emailId, String phoneNo) { + boolean validEmailId = Utils.validateEmailId(emailId); + if(!validEmailId) { + System.out.println("Please enter valid emailId."); + return; + } + boolean validPhoneNo = Utils.validatePhoneNo(phoneNo); + if(!validPhoneNo) { + System.out.println("Please enter valid phoneNo."); + return; + } + // TODO - Check username is not already used. If used, do not create new buyer and return. + String buyerId = this.generateBuyerId(); + Buyer buyer = new Buyer(buyerId, username, emailId, phoneNo); + buyerRepository.addDataToRepository(buyerId, buyer); + } + + public List viewAllBuyerBids(String buyerId) { + boolean validate = this.validateBuyer(buyerId); + if(!validate) { + System.out.println("Buyer Id not valid"); + return null; + } + Buyer buyer = buyerRepository.getData(buyerId); + List response = new LinkedList<>(); + Map> subscribedAuctions = buyer.getSubscribedAuctions(); + for(String auctionId : subscribedAuctions.keySet()) { + for(String productId : subscribedAuctions.get(auctionId).keySet()) { + String data = "AuctiondId: " + auctionId + ", " + + "ProductId: " + productId + ", " + + "BidValue: " + subscribedAuctions.get(auctionId).get(productId); + response.add(data); + } + } + return response; + } + + public void subscribeToAuction(String buyerId, String auctionId) { + boolean validate = this.validateBuyer(buyerId); + if(!validate) { + System.out.println("Buyer Id not valid"); + return; + } + Buyer buyer = buyerRepository.getData(buyerId); + if(buyer.getSubscribedAuctions().containsKey(auctionId)) { + System.out.println("Already subscribed to the auction."); + return; + } + boolean success = auctionService.subscribe(auctionId, buyerId, BUYER_AS_OBSERVER); + if(success) { + buyer.getSubscribedAuctions().put(auctionId, new HashMap<>()); + } + else { + System.out.println("Failed to subscribe."); + } + } + + public void unsubscribeToAuction(String buyerId, String auctionId) { + boolean validate = this.validateBuyer(buyerId); + if(!validate) { + System.out.println("Buyer Id not valid"); + return; + } + Buyer buyer = buyerRepository.getData(buyerId); + if(!buyer.getSubscribedAuctions().containsKey(auctionId)) { + System.out.println("Have not subscribed to the auction."); + return; + } + boolean success = auctionService.unsubscribe(auctionId, buyerId, BUYER_AS_OBSERVER); + if(success) { + buyer.getSubscribedAuctions().remove(auctionId); + } + else { + System.out.println("Failed to unsubscribe."); + } + } + + //Access only from AuctionService + public boolean placeBid(String buyerId, String auctionId, String productId, double bidValue) { + boolean validate = this.validateBuyer(buyerId); + if(!validate) { + System.out.println("Buyer Id not valid"); + return false; + } + subscribeToAuction(buyerId, auctionId); + Buyer buyer = buyerRepository.getData(buyerId); + Map> subscribedAuctions = buyer.getSubscribedAuctions(); + if(subscribedAuctions.containsKey(auctionId)) { + Map existingBids = subscribedAuctions.get(auctionId); + if(existingBids.containsKey(productId)) { + double exisitingBidValue = existingBids.get(productId); + if(exisitingBidValue <= bidValue) { + System.out.println("You have already bid with less/same amount. Increase bid."); + return false; + } + } + existingBids.put(productId, bidValue); + } + else { + System.out.println("subscribeToAuction call failed."); + return false; + } + return true; + } + + private static final int MAX_NOTIFICATION_COUNT = 10; //Maximum no. of notifications to return + + public List getNotifications(String buyerId) { + boolean validate = this.validateBuyer(buyerId); + if(!validate) { + System.out.println("Buyer Id not valid"); + return null; + } + Buyer buyer = buyerRepository.getData(buyerId); + List retval = new LinkedList<>(); + for(Notification notification : buyer.getNotifications()) { + retval.add(notification.toString()); + if(retval.size() == MAX_NOTIFICATION_COUNT) { + break; + } + } + return retval; + } + + public boolean validateBuyer(String buyerId) { + return buyerRepository.getData(buyerId) != null ? true : false; + } + + @Override + public void updateObserver(List buyerIds, Event event) { + for(String buyerId : buyerIds) { + boolean validate = this.validateBuyer(buyerId); + if(!validate) { + continue; + } + Buyer buyer = buyerRepository.getData(buyerId); + Notification notification = new Notification(event.getMessage(), event.getDateTime()); + buyer.getNotifications().add(0, notification); + } + } + +} diff --git a/src/main/java/lld/auctionSystem/services/ObserverService.java b/src/main/java/lld/auctionSystem/services/ObserverService.java new file mode 100644 index 0000000..5223802 --- /dev/null +++ b/src/main/java/lld/auctionSystem/services/ObserverService.java @@ -0,0 +1,11 @@ +package lld.auctionSystem.services; + +import lld.auctionSystem.models.Event; + +import java.util.List; + +public interface ObserverService { + + void updateObserver(List ids, Event event); + +} diff --git a/src/main/java/lld/auctionSystem/services/PublisherService.java b/src/main/java/lld/auctionSystem/services/PublisherService.java new file mode 100644 index 0000000..9343401 --- /dev/null +++ b/src/main/java/lld/auctionSystem/services/PublisherService.java @@ -0,0 +1,16 @@ +package lld.auctionSystem.services; + + +import lld.auctionSystem.enums.ObserverType; +import lld.auctionSystem.models.Event; + +import java.util.List; + +public interface PublisherService { + + void updateObservers(List observerIds, ObserverType type, Event event); + + boolean subscribe(String subjectId, String observerId, ObserverType type); + + boolean unsubscribe(String subjectId, String observerId, ObserverType type); +} diff --git a/src/main/java/lld/auctionSystem/services/SellerService.java b/src/main/java/lld/auctionSystem/services/SellerService.java new file mode 100644 index 0000000..e19a8cd --- /dev/null +++ b/src/main/java/lld/auctionSystem/services/SellerService.java @@ -0,0 +1,94 @@ +package lld.auctionSystem.services; + + +import lld.auctionSystem.models.Event; +import lld.auctionSystem.models.Notification; +import lld.auctionSystem.models.Seller; +import lld.auctionSystem.repository.SellerRepository; +import lld.auctionSystem.utils.Utils; + +import java.util.LinkedList; +import java.util.List; +import java.util.UUID; + +public class SellerService implements ObserverService { + SellerRepository sellerRepository = SellerRepository.getInstance(); + AuctionService auctionService = new AuctionService(); + + private String generateSellerId() { + return UUID.randomUUID().toString()+".SI"; + } + + public void addSeller( String username, String emailId, String phoneNo) { + boolean validEmailId = Utils.validateEmailId(emailId); + if(!validEmailId) { + System.out.println("Please enter valid emailId."); + return; + } + boolean validPhoneNo = Utils.validatePhoneNo(phoneNo); + if(!validPhoneNo) { + System.out.println("Please enter valid phoneNo."); + return; + } + // TODO - Check username is not already used. If used, do not create new seller and return. + String sellerId = this.generateSellerId(); + Seller seller = new Seller(sellerId, username, emailId, phoneNo); + sellerRepository.addDataToRepository(sellerId, seller); + } + + public List viewAllSellerOwnedAuctions(String sellerId) { + boolean validate = this.validateSeller(sellerId); + if(!validate) { + System.out.println("Seller Id not valid"); + return null; + } + Seller seller = sellerRepository.getData(sellerId); + List response = new LinkedList<>(); + for(String auctionId : seller.getAuctions()) { + String auctionDetails = auctionService.getAuctionDetails(sellerId, auctionId, "SellerView"); + if(auctionDetails==null) { + continue; + } + response.add(auctionDetails); + } + return response; + } + + private static final int MAX_NOTIFICATION_COUNT = 10; //Maximum no. of notifications to return + + public List getNotifications(String sellerId) { + boolean validate = this.validateSeller(sellerId); + if(!validate) { + System.out.println("Seller Id not valid"); + return null; + } + Seller seller = sellerRepository.getData(sellerId); + List retval = new LinkedList<>(); + for(Notification notification : seller.getNotifications()) { + retval.add(notification.toString()); + if(retval.size() == MAX_NOTIFICATION_COUNT) { + break; + } + } + return retval; + } + + public boolean validateSeller(String sellerId) { + return sellerRepository.getData(sellerId) != null ? true : false; + } + + @Override + //This method will be called by the publisher to update the observer. + public void updateObserver(List sellerIds, Event event) { + for(String sellerId : sellerIds) { + boolean validate = this.validateSeller(sellerId); + if(!validate) { + continue; + } + Seller seller = sellerRepository.getData(sellerId); + Notification notification = new Notification(event.getMessage(), event.getDateTime()); + seller.getNotifications().add(0, notification); + } + } + +} diff --git a/src/main/java/lld/auctionSystem/services/strategies/AuctionDetails.java b/src/main/java/lld/auctionSystem/services/strategies/AuctionDetails.java new file mode 100644 index 0000000..21e6888 --- /dev/null +++ b/src/main/java/lld/auctionSystem/services/strategies/AuctionDetails.java @@ -0,0 +1,5 @@ +package lld.auctionSystem.services.strategies; + +public abstract class AuctionDetails { + public abstract String getAuctionDetails(String userId, String auctionId); +} diff --git a/src/main/java/lld/auctionSystem/services/strategies/BuyerViewAuctionDetails.java b/src/main/java/lld/auctionSystem/services/strategies/BuyerViewAuctionDetails.java new file mode 100644 index 0000000..c2c904b --- /dev/null +++ b/src/main/java/lld/auctionSystem/services/strategies/BuyerViewAuctionDetails.java @@ -0,0 +1,45 @@ +package lld.auctionSystem.services.strategies; + + +import lld.auctionSystem.enums.AuctionProductState; +import lld.auctionSystem.enums.AuctionState; +import lld.auctionSystem.models.Auction; +import lld.auctionSystem.models.AuctionProduct; +import lld.auctionSystem.models.Product; +import lld.auctionSystem.repository.AuctionRepository; +import lld.auctionSystem.repository.ProductRepository; + +public class BuyerViewAuctionDetails extends AuctionDetails{ + AuctionRepository auctionRepository = AuctionRepository.getInstance(); + ProductRepository productRepository = ProductRepository.getInstance(); + + public String getAuctionDetails(String buyerId, String auctionId) { + Auction auction = auctionRepository.getData(auctionId); + if(auction.getAuctionState()== AuctionState.STARTED) { + StringBuilder data = new StringBuilder(); + data.append("Auction start Date & Time" + auction.getStartDate().toString() + "\n"); + data.append("Auction end Date & Time" + auction.getEndDate().toString() + "\n"); + for (AuctionProduct auctionProduct : auction.getProducts()) { + Product product = productRepository.getData(auctionProduct.getProductId()); + data.append("Auction Product : " + product.getName() + ", " + + product.getDescription() + ", "); + if (auctionProduct.getAuctionProductState() == AuctionProductState.SOLD) { + data.append("State: SOLD"); + } else { + data.append("State: UNSOLD" + ", "); + if (auctionProduct.getAuctionProductState() == AuctionProductState.UNBID) { + data.append("Bid starts at: " + "No bid placed till now"); + } else { + data.append("Current Bid: " + auctionProduct.getCurrentBid()); + } + } + data.append("\n"); + } + return data.toString(); + } + else { + System.out.println("Error! Auction should not be visible to the user."); + return null; + } + } +} diff --git a/src/main/java/lld/auctionSystem/services/strategies/SellerViewAuctionDetails.java b/src/main/java/lld/auctionSystem/services/strategies/SellerViewAuctionDetails.java new file mode 100644 index 0000000..46f4687 --- /dev/null +++ b/src/main/java/lld/auctionSystem/services/strategies/SellerViewAuctionDetails.java @@ -0,0 +1,46 @@ +package lld.auctionSystem.services.strategies; + +import lld.auctionSystem.enums.AuctionProductState; +import lld.auctionSystem.models.Auction; +import lld.auctionSystem.models.AuctionProduct; +import lld.auctionSystem.models.Product; +import lld.auctionSystem.repository.AuctionRepository; +import lld.auctionSystem.repository.ProductRepository; + +public class SellerViewAuctionDetails extends AuctionDetails { + AuctionRepository auctionRepository = AuctionRepository.getInstance(); + ProductRepository productRepository = ProductRepository.getInstance(); + + public String getAuctionDetails(String sellerId, String auctionId) { + Auction auction = auctionRepository.getData(auctionId); + if (!auction.getSellerId().equals(sellerId)) { + // TODO - Throw Exception + System.out.println("This auction does not belong to the seller who requested auction data."); + return null; + } + StringBuilder data = new StringBuilder(""); + data.append("Auction State : " + auction.getAuctionState().toString() + "\n"); + data.append("Auction start Date & Time" + auction.getStartDate().toString() + "\n"); + data.append("Auction end Date & Time" + auction.getEndDate().toString() + "\n"); + if (auction.getProducts() != null) { + for (AuctionProduct auctionProduct : auction.getProducts()) { + Product product = productRepository.getData(auctionProduct.getProductId()); + data.append("Auction Product : " + product.getName() + ", " + + product.getDescription() + ", " + + auctionProduct.getBasePrice() + ", " + + auctionProduct.getAuctionProductState().toString() + ", "); + if(auctionProduct.getAuctionProductState() != AuctionProductState.UNBID) { + data.append(auctionProduct.getCurrentBid()); + } + else { + data.append("No bid on product yet"); + } + data.append("\n"); + } + } + else { + data.append("Auction Product : No product added\n"); + } + return data.toString(); + } +} diff --git a/src/main/java/lld/auctionSystem/utils/Utils.java b/src/main/java/lld/auctionSystem/utils/Utils.java new file mode 100644 index 0000000..bf6160d --- /dev/null +++ b/src/main/java/lld/auctionSystem/utils/Utils.java @@ -0,0 +1,15 @@ +package lld.auctionSystem.utils; + +public class Utils { + + public static boolean validateEmailId(String emailId) { + // TODO - Logic to validate emailId. + return true; + } + + public static boolean validatePhoneNo(String phoneNo) { + // TODO - Logic to validate phoneNo. + return true; + } + +} diff --git a/src/main/java/lld/billsharing/BillSharingMain.java b/src/main/java/lld/billsharing/BillSharingMain.java new file mode 100644 index 0000000..431fc3d --- /dev/null +++ b/src/main/java/lld/billsharing/BillSharingMain.java @@ -0,0 +1,110 @@ +package lld.billsharing; + +import lld.billsharing.exceptions.ContributionExceededException; +import lld.billsharing.exceptions.ExpenseDoesNotExistsException; +import lld.billsharing.exceptions.ExpenseSettledException; +import lld.billsharing.exceptions.InvalidExpenseState; +import lld.billsharing.model.Contribution; +import lld.billsharing.model.Expense; +import lld.billsharing.model.ExpenseGroup; +import lld.billsharing.model.ExpenseStatus; +import lld.billsharing.model.User; +import lld.billsharing.model.UserShare; +import lld.billsharing.repository.ExpenseRepository; +import lld.billsharing.service.ExpenseService; +import lld.billsharing.service.UserService; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.Month; +import java.util.Set; + +public class BillSharingMain { + + static ExpenseService expenseService; + static UserService userService; + + public static void main(String[] args) throws ContributionExceededException, InvalidExpenseState, + ExpenseSettledException { + expenseService = new ExpenseService(); + userService = new UserService(); + createTestUsers(); + + Expense expense = createLunchExpense(); + try { + bifurcateExpense(expense.getId()); + } catch (ExpenseDoesNotExistsException expenseDoesNotExistsException) { + System.out.println(expenseDoesNotExistsException.getMessage()); + } + expense.setExpenseStatus(ExpenseStatus.PENDING); + + Set users = expense.getExpenseGroup().getGroupMembers(); + for (User user : users) { + contributeToExpense(expense.getId(), user.getEmailId()); + } + if (expenseService.isExpenseSettled(expense.getId())) { + System.out.println("Expense Settled...."); + expenseService.setExpenseStatus(expense.getId(), ExpenseStatus.SETTLED); + } + System.out.println("Bye......"); + } + + private static void createTestUsers() { + User user1 = userService.createUser("bagesh@gmail.com", "bagesh", "3486199635"); + User user2 = userService.createUser("ajay@gmail.com", "ajay", "6112482630"); + User user3 = userService.createUser("amit@gmail.com", "amit", "2509699232"); + User user4 = userService.createUser("kamal@gmail.com", "kamal", "5816355154"); + User user5 = userService.createUser("neha@gmail.com", "neha", "7737316054"); + User user6 = userService.createUser("kajal@gmail.com", "kajal", "4813053349"); + User user7 = userService.createUser("jyothi@gmail.com", "jyothi", "3974178644"); + User user8 = userService.createUser("subin@gmail.com", "subin", "4768463294"); + User user9 = userService.createUser("deepak@gmail.com", "deepak", "4829338803"); + User user10 = userService.createUser("vishnu@gmail.com", "vishnu", "3384071602"); + User user11 = userService.createUser("mayank@gmail.com", "mayank", "2376951206"); + User user12 = userService.createUser("anu@gmail.com", "anu", "8478577491"); + User user13 = userService.createUser("kavya@gmail.com", "kavya", "7505888698"); + User user14 = userService.createUser("divya@gmail.com", "divya", "9587030077"); + User user15 = userService.createUser("prabhu@gmail.com", "prabhu", "3303167757"); + User user16 = userService.createUser("sangeeth@gmail.com", "sangeeth", "7409081739"); + User user17 = userService.createUser("rajesh@gmail.com", "rajesh", "2367659285"); + User user18 = userService.createUser("alamelu@gmail.com", "alamelu", "8938025834"); + User user19 = userService.createUser("aruna@gmail.com", "aruna", "8189506064"); + User user20 = userService.createUser("palani@gmail.com", "palani", "2973733105"); + } + + public static Expense createLunchExpense() { + Expense expense = expenseService.createExpense("Team Lunch", + "Friday 19Th June Lunch in Briyani zone" + , LocalDateTime.of(2020, Month.JUNE, 19, 12, 0), + 2000.00, "vishnu@gmail.com"); + return expense; + } + + private static void bifurcateExpense(String expenseId) throws ExpenseDoesNotExistsException { + expenseService.addUsersToExpense(expenseId, "bagesh@gmail.com"); + expenseService.addUsersToExpense(expenseId, "divya@gmail.com"); + expenseService.addUsersToExpense(expenseId, "palani@gmail.com"); + expenseService.addUsersToExpense(expenseId, "neha@gmail.com"); + + expenseService.assignExpenseShare(expenseId, + ExpenseRepository.expenseMap.get(expenseId).getUserId(), 400); + expenseService.assignExpenseShare(expenseId, "bagesh@gmail.com", 400); + expenseService.assignExpenseShare(expenseId, "divya@gmail.com", 400); + expenseService.assignExpenseShare(expenseId, "palani@gmail.com", 400); + expenseService.assignExpenseShare(expenseId, "neha@gmail.com", 400); + } + + private static void contributeToExpense(String expenseId, String userId) + throws ContributionExceededException, InvalidExpenseState, ExpenseSettledException { + Contribution contribution = new Contribution(); + Expense expense = ExpenseRepository.expenseMap.get(expenseId); + ExpenseGroup expenseGroup = expense.getExpenseGroup(); + UserShare userShare = expenseGroup.getUserContributions().get(userId); + contribution.setContributionValue(userShare.getShare()); + contribution.setContributionDate(LocalDateTime.now()); + contribution.setTransactionId("T" + Instant.EPOCH); + contribution.setTransactionDescription("Transferred from UPI"); + userService.contributeToExpense(expenseId, userId, contribution); + + } +} diff --git a/src/main/java/lld/billsharing/exceptions/ContributionExceededException.java b/src/main/java/lld/billsharing/exceptions/ContributionExceededException.java new file mode 100644 index 0000000..1d0bbff --- /dev/null +++ b/src/main/java/lld/billsharing/exceptions/ContributionExceededException.java @@ -0,0 +1,12 @@ +package lld.billsharing.exceptions; + +public class ContributionExceededException extends Exception { + public ContributionExceededException(String message) { + super(message); + } + + @Override + public String getMessage() { + return super.getMessage(); + } +} diff --git a/src/main/java/lld/billsharing/exceptions/ExpenseDoesNotExistsException.java b/src/main/java/lld/billsharing/exceptions/ExpenseDoesNotExistsException.java new file mode 100644 index 0000000..a935961 --- /dev/null +++ b/src/main/java/lld/billsharing/exceptions/ExpenseDoesNotExistsException.java @@ -0,0 +1,13 @@ +package lld.billsharing.exceptions; + +public class ExpenseDoesNotExistsException extends Exception { + + public ExpenseDoesNotExistsException(String message) { + super(message); + } + + @Override + public String getMessage() { + return this.getMessage(); + } +} diff --git a/src/main/java/lld/billsharing/exceptions/ExpenseSettledException.java b/src/main/java/lld/billsharing/exceptions/ExpenseSettledException.java new file mode 100644 index 0000000..761ba48 --- /dev/null +++ b/src/main/java/lld/billsharing/exceptions/ExpenseSettledException.java @@ -0,0 +1,12 @@ +package lld.billsharing.exceptions; + +public class ExpenseSettledException extends Exception { + public ExpenseSettledException(String s) { + super(s); + } + + @Override + public String getMessage() { + return super.getMessage(); + } +} diff --git a/src/main/java/lld/billsharing/exceptions/InvalidExpenseState.java b/src/main/java/lld/billsharing/exceptions/InvalidExpenseState.java new file mode 100644 index 0000000..1a01a90 --- /dev/null +++ b/src/main/java/lld/billsharing/exceptions/InvalidExpenseState.java @@ -0,0 +1,13 @@ +package lld.billsharing.exceptions; + +public class InvalidExpenseState extends Exception { + + public InvalidExpenseState(String message) { + super(message); + } + + @Override + public String getMessage() { + return super.getMessage(); + } +} diff --git a/src/main/java/lld/billsharing/model/Contribution.java b/src/main/java/lld/billsharing/model/Contribution.java new file mode 100644 index 0000000..716ba8d --- /dev/null +++ b/src/main/java/lld/billsharing/model/Contribution.java @@ -0,0 +1,16 @@ +package lld.billsharing.model; + +import lombok.Getter; +import lombok.Setter; + +import java.time.LocalDateTime; + +@Getter +@Setter +public class Contribution { + private String contributionId; + private double contributionValue; + private String transactionId; + private LocalDateTime contributionDate; + private String transactionDescription; +} \ No newline at end of file diff --git a/src/main/java/lld/billsharing/model/Expense.java b/src/main/java/lld/billsharing/model/Expense.java new file mode 100644 index 0000000..5ceeee2 --- /dev/null +++ b/src/main/java/lld/billsharing/model/Expense.java @@ -0,0 +1,22 @@ +package lld.billsharing.model; + + +import lombok.*; + +import java.time.LocalDateTime; + +@Getter +@Setter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class Expense { + private String id; + private String userId; + private String title; + private String description; + private LocalDateTime expenseDate; + private ExpenseStatus expenseStatus; + private double expenseAmount; + private ExpenseGroup expenseGroup; +} \ No newline at end of file diff --git a/src/main/java/lld/billsharing/model/ExpenseGroup.java b/src/main/java/lld/billsharing/model/ExpenseGroup.java new file mode 100644 index 0000000..18704b5 --- /dev/null +++ b/src/main/java/lld/billsharing/model/ExpenseGroup.java @@ -0,0 +1,26 @@ +package lld.billsharing.model; + +import lombok.Getter; +import lombok.Setter; + +import java.util.*; + +@Getter +public class ExpenseGroup { + + private Set groupMembers = new HashSet<>(); + + public ExpenseGroup() { + this.expenseGroupId = UUID.randomUUID().toString(); + this.groupMembers = new HashSet<>(); + this.userContributions = new HashMap<>(); + } + + private String expenseGroupId; + @Setter + private Map userContributions; + + public void ExpenseGroup() { + + } +} \ No newline at end of file diff --git a/src/main/java/lld/billsharing/model/ExpenseStatus.java b/src/main/java/lld/billsharing/model/ExpenseStatus.java new file mode 100644 index 0000000..88c7127 --- /dev/null +++ b/src/main/java/lld/billsharing/model/ExpenseStatus.java @@ -0,0 +1,7 @@ +package lld.billsharing.model; + +public enum ExpenseStatus { + CREATED, + PENDING, + SETTLED +} diff --git a/src/main/java/lld/billsharing/model/User.java b/src/main/java/lld/billsharing/model/User.java new file mode 100644 index 0000000..6ad2d2f --- /dev/null +++ b/src/main/java/lld/billsharing/model/User.java @@ -0,0 +1,23 @@ +package lld.billsharing.model; + +import lombok.Getter; +import lombok.NonNull; +import lombok.Setter; + +import java.util.UUID; + +@Getter +@Setter +public class User { + private String name; + private String userId; + private String emailId; + private String phoneNumber; + + public User(@NonNull String emailId, String name, String phoneNumber) { + userId = UUID.randomUUID().toString(); + this.emailId = emailId; + this.name = name; + this.phoneNumber = phoneNumber; + } +} \ No newline at end of file diff --git a/src/main/java/lld/billsharing/model/UserShare.java b/src/main/java/lld/billsharing/model/UserShare.java new file mode 100644 index 0000000..4c833e0 --- /dev/null +++ b/src/main/java/lld/billsharing/model/UserShare.java @@ -0,0 +1,19 @@ +package lld.billsharing.model; + +import lombok.Getter; + +import java.util.ArrayList; +import java.util.List; + +@Getter +public class UserShare { + public UserShare(String userId, double share) { + this.userId = userId; + this.share = share; + contributions = new ArrayList<>(); + } + + private String userId; + private double share; + List contributions; +} diff --git a/src/main/java/lld/billsharing/repository/ExpenseRepository.java b/src/main/java/lld/billsharing/repository/ExpenseRepository.java new file mode 100644 index 0000000..406b7c8 --- /dev/null +++ b/src/main/java/lld/billsharing/repository/ExpenseRepository.java @@ -0,0 +1,14 @@ +package lld.billsharing.repository; + +import lld.billsharing.model.Expense; +import lombok.Getter; +import lombok.Setter; + +import java.util.HashMap; +import java.util.Map; + +@Getter +@Setter +public class ExpenseRepository { + public static Map expenseMap = new HashMap<>(); +} diff --git a/src/main/java/lld/billsharing/repository/UserRepository.java b/src/main/java/lld/billsharing/repository/UserRepository.java new file mode 100644 index 0000000..833f3a9 --- /dev/null +++ b/src/main/java/lld/billsharing/repository/UserRepository.java @@ -0,0 +1,14 @@ +package lld.billsharing.repository; + +import lld.billsharing.model.User; +import lombok.Getter; +import lombok.Setter; + +import java.util.HashMap; +import java.util.Map; + +@Getter +@Setter +public class UserRepository { + public static Map userHashMap = new HashMap<>(); +} diff --git a/src/main/java/lld/billsharing/service/ExpenseService.java b/src/main/java/lld/billsharing/service/ExpenseService.java new file mode 100644 index 0000000..a3c588c --- /dev/null +++ b/src/main/java/lld/billsharing/service/ExpenseService.java @@ -0,0 +1,88 @@ +package lld.billsharing.service; + +import lld.billsharing.exceptions.ExpenseDoesNotExistsException; +import lld.billsharing.model.Contribution; +import lld.billsharing.model.Expense; +import lld.billsharing.model.ExpenseGroup; +import lld.billsharing.model.ExpenseStatus; +import lld.billsharing.model.UserShare; +import lld.billsharing.repository.ExpenseRepository; +import lld.billsharing.repository.UserRepository; + +import java.time.LocalDateTime; +import java.util.Map; +import java.util.UUID; + +//@Slf4j +public class ExpenseService { + private NotificationService notificationService = new NotificationServiceImpl(); + + public Expense createExpense(String title, String description, LocalDateTime expenseDate, double expenseAmount, + String userId) { + Expense expense = Expense.builder() + .id(UUID.randomUUID().toString()) + .title(title) + .description(description) + .expenseDate(expenseDate) + .expenseAmount(expenseAmount) + .userId(userId) + .expenseStatus(ExpenseStatus.CREATED) + .expenseGroup(new ExpenseGroup()) + .build(); + ExpenseRepository.expenseMap.putIfAbsent(expense.getId(), expense); + return expense; + } + + public void addUsersToExpense(String expenseId, String emailId) throws + ExpenseDoesNotExistsException { + if (!ExpenseRepository.expenseMap.containsKey(expenseId)) { + throw new + ExpenseDoesNotExistsException("Better create expense and come here...."); +// System.out.println("Better create expense and come here...."); + } + ExpenseRepository.expenseMap.get(expenseId) + .getExpenseGroup().getGroupMembers() + .add(UserRepository.userHashMap.get(emailId)); + + if (notificationService != null) { + notificationService.notifyUser(UserRepository.userHashMap.get(emailId), + ExpenseRepository.expenseMap.get(expenseId)); + } + } + + public void assignExpenseShare(String expenseId, String emailId, double share) + throws ExpenseDoesNotExistsException { + if (!ExpenseRepository.expenseMap.containsKey(expenseId)) { + throw new ExpenseDoesNotExistsException(String.format("Expense %s does not exists", expenseId)); + } + Expense expense = ExpenseRepository.expenseMap.get(expenseId); + expense.getExpenseGroup() + .getUserContributions().putIfAbsent(emailId, new UserShare(emailId, share)); + + } + + public void setExpenseStatus(String expenseId, ExpenseStatus expenseStatus) { + Expense expense = ExpenseRepository.expenseMap.get(expenseId); + expense.setExpenseStatus(expenseStatus); + } + + public boolean isExpenseSettled(String expenseId) { + Expense expense = ExpenseRepository.expenseMap.get(expenseId); + ExpenseGroup expenseGroup = expense.getExpenseGroup(); + Map userContributions = expenseGroup.getUserContributions(); + + double total = expense.getExpenseAmount(); + + for (Map.Entry entry : userContributions.entrySet()) { + UserShare userShare = entry.getValue(); + for (Contribution contribution : userShare.getContributions()) { + total -= contribution.getContributionValue(); + } + } + if (total <= 1) { + return true; + } + return false; + } + +} diff --git a/src/main/java/lld/billsharing/service/NotificationService.java b/src/main/java/lld/billsharing/service/NotificationService.java new file mode 100644 index 0000000..73abdda --- /dev/null +++ b/src/main/java/lld/billsharing/service/NotificationService.java @@ -0,0 +1,8 @@ +package lld.billsharing.service; + +import lld.billsharing.model.Expense; +import lld.billsharing.model.User; + +public interface NotificationService { + void notifyUser(User user, Expense expense); +} diff --git a/src/main/java/lld/billsharing/service/NotificationServiceImpl.java b/src/main/java/lld/billsharing/service/NotificationServiceImpl.java new file mode 100644 index 0000000..7f5bfbb --- /dev/null +++ b/src/main/java/lld/billsharing/service/NotificationServiceImpl.java @@ -0,0 +1,12 @@ +package lld.billsharing.service; + + +import lld.billsharing.model.Expense; +import lld.billsharing.model.User; + +public class NotificationServiceImpl implements NotificationService { + @Override + public void notifyUser(User user, Expense expense) { + System.out.println("Notified"); + } +} diff --git a/src/main/java/lld/billsharing/service/UserService.java b/src/main/java/lld/billsharing/service/UserService.java new file mode 100644 index 0000000..9a0ff99 --- /dev/null +++ b/src/main/java/lld/billsharing/service/UserService.java @@ -0,0 +1,61 @@ +package lld.billsharing.service; + +import lld.billsharing.exceptions.ContributionExceededException; +import lld.billsharing.exceptions.ExpenseSettledException; +import lld.billsharing.exceptions.InvalidExpenseState; +import lld.billsharing.model.Contribution; +import lld.billsharing.model.Expense; +import lld.billsharing.model.ExpenseGroup; +import lld.billsharing.model.ExpenseStatus; +import lld.billsharing.model.User; +import lld.billsharing.model.UserShare; +import lld.billsharing.repository.ExpenseRepository; +import lld.billsharing.repository.UserRepository; + +public class UserService { + public User createUser(String emailId, String name, String phoneNumber) { + User user = new User(emailId, name, phoneNumber); + UserRepository.userHashMap.putIfAbsent(emailId, user); + return user; + } + + public void contributeToExpense(String expenseId, String emailId, + Contribution contribution) + throws InvalidExpenseState, ExpenseSettledException, ContributionExceededException { + Expense expense = ExpenseRepository.expenseMap.get(expenseId); + ExpenseGroup expenseGroup = expense.getExpenseGroup(); + if (expense.getExpenseStatus() == ExpenseStatus.CREATED) { + throw new InvalidExpenseState("Invalid expense State"); + } + if (expense.getExpenseStatus() == ExpenseStatus.SETTLED) { + throw new ExpenseSettledException("Expense is already settled."); + } + UserShare userShare = expenseGroup.getUserContributions().get(emailId); + if (contribution.getContributionValue() > userShare.getShare()) { + throw new ContributionExceededException( + String.format("User %s contribution %d exceeded the share %d", + emailId, contribution.getContributionValue(), userShare.getShare())); + } + userShare.getContributions().add(contribution); + } + + public void contributeToExpense(String expenseId, String emailId, String toEmailId, + Contribution contribution) + throws InvalidExpenseState, ExpenseSettledException, ContributionExceededException { + Expense expense = ExpenseRepository.expenseMap.get(expenseId); + ExpenseGroup expenseGroup = expense.getExpenseGroup(); + if (expense.getExpenseStatus() == ExpenseStatus.CREATED) { + throw new InvalidExpenseState("Invalid expense State"); + } + if (expense.getExpenseStatus() == ExpenseStatus.SETTLED) { + throw new ExpenseSettledException("Expense is already settled."); + } + UserShare userShare = expenseGroup.getUserContributions().get(emailId); + if (contribution.getContributionValue() > userShare.getShare()) { + throw new ContributionExceededException( + String.format("User %s contribution %d exceeded the share %d", + emailId, contribution.getContributionValue(), userShare.getShare())); + } + userShare.getContributions().add(contribution); + } +} diff --git a/src/main/java/lld/elevator/TestElevator.java b/src/main/java/lld/elevator/TestElevator.java new file mode 100644 index 0000000..f39b091 --- /dev/null +++ b/src/main/java/lld/elevator/TestElevator.java @@ -0,0 +1,413 @@ +package lld.elevator; + +import java.util.TreeSet; + +class Elevator { + private Direction currentDirection = Direction.UP; + private State currentState = State.IDLE; + private int currentFloor = 0; + + /** + * jobs which are being processed + */ + private TreeSet currentJobs = new TreeSet<>(); + /** + * up jobs which cannot be processed now so put in pending queue + */ + private TreeSet upPendingJobs = new TreeSet<>(); + /** + * down jobs which cannot be processed now so put in pending queue + */ + private TreeSet downPendingJobs = new TreeSet<>(); + + public void startElevator() { + System.out.println("The Elevator has started functioning"); + while (true) { + + if (checkIfJob()) { + + if (currentDirection == Direction.UP) { + Request request = currentJobs.pollFirst(); + processUpRequest(request); + if (currentJobs.isEmpty()) { + addPendingDownJobsToCurrentJobs(); + + } + + } + if (currentDirection == Direction.DOWN) { + Request request = currentJobs.pollLast(); + processDownRequest(request); + if (currentJobs.isEmpty()) { + addPendingUpJobsToCurrentJobs(); + } + + } + } + } + } + + public boolean checkIfJob() { + + return !currentJobs.isEmpty(); + + } + + private void processUpRequest(Request request) { + + int startFloor = currentFloor; + // The elevator is not on the floor where the person has requested it i.e. source floor. So first bring it there. + if (startFloor < request.getExternalRequest().getSourceFloor()) { + for (int i = startFloor; i <= request.getExternalRequest().getSourceFloor(); i++) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + + e.printStackTrace(); + } + System.out.println("We have reached floor -- " + i); + currentFloor = i; + } + } + // The elevator is now on the floor where the person has requested it i.e. source floor. User can enter and go to the destination floor. + System.out.println("Reached Source Floor--opening door"); + + startFloor = currentFloor; + + for (int i = startFloor + 1; i <= request.getInternalRequest().getDestinationFloor(); i++) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + + e.printStackTrace(); + } + System.out.println("We have reached floor -- " + i); + currentFloor = i; + if (checkIfNewJobCanBeProcessed(request)) { + break; + } + } + + } + + private void processDownRequest(Request request) { + + int startFloor = currentFloor; + if (startFloor < request.getExternalRequest().getSourceFloor()) { + for (int i = startFloor; i <= request.getExternalRequest().getSourceFloor(); i++) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + + e.printStackTrace(); + } + System.out.println("We have reached floor -- " + i); + currentFloor = i; + } + } + + System.out.println("Reached Source Floor--opening door"); + + startFloor = currentFloor; + + for (int i = startFloor - 1; i >= request.getInternalRequest().getDestinationFloor(); i--) { + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + + e.printStackTrace(); + } + System.out.println("We have reached floor -- " + i); + currentFloor = i; + if (checkIfNewJobCanBeProcessed(request)) { + break; + } + } + + } + + private boolean checkIfNewJobCanBeProcessed(Request currentRequest) { + if (checkIfJob()) { + if (currentDirection == Direction.UP) { + Request request = currentJobs.pollLast(); + if (request.getInternalRequest().getDestinationFloor() < currentRequest.getInternalRequest() + .getDestinationFloor()) { + currentJobs.add(request); + currentJobs.add(currentRequest); + return true; + } + currentJobs.add(request); + + } + + if (currentDirection == Direction.DOWN) { + Request request = currentJobs.pollFirst(); + if (request.getInternalRequest().getDestinationFloor() > currentRequest.getInternalRequest() + .getDestinationFloor()) { + currentJobs.add(request); + currentJobs.add(currentRequest); + return true; + } + currentJobs.add(request); + + } + + } + return false; + + } + + private void addPendingDownJobsToCurrentJobs() { + if (!downPendingJobs.isEmpty()) { + System.out.println("Pick a pending down job and execute it by putting in current job"); + currentJobs = downPendingJobs; + currentDirection = Direction.DOWN; + } else { + currentState = State.IDLE; + System.out.println("The elevator is in Idle state"); + } + + } + + private void addPendingUpJobsToCurrentJobs() { + if (!upPendingJobs.isEmpty()) { + System.out.println("Pick a pending up job and execute it by putting in current job"); + + currentJobs = upPendingJobs; + currentDirection = Direction.UP; + } else { + currentState = State.IDLE; + System.out.println("The elevator is in Idle state"); + + } + + } + + public void addJob(Request request) { + if (currentState == State.IDLE) { + currentState = State.MOVING; + currentDirection = request.getExternalRequest().getDirectionToGo(); + currentJobs.add(request); + } else if (currentState == State.MOVING) { + + if (request.getExternalRequest().getDirectionToGo() != currentDirection) { + addtoPendingJobs(request); + } else if (request.getExternalRequest().getDirectionToGo() == currentDirection) { + if (currentDirection == Direction.UP + && request.getInternalRequest().getDestinationFloor() < currentFloor) { + addtoPendingJobs(request); + } else if (currentDirection == Direction.DOWN + && request.getInternalRequest().getDestinationFloor() > currentFloor) { + addtoPendingJobs(request); + } else { + currentJobs.add(request); + } + + } + + } + + } + + public void addtoPendingJobs(Request request) { + if (request.getExternalRequest().getDirectionToGo() == Direction.UP) { + System.out.println("Add to pending up jobs"); + upPendingJobs.add(request); + } else { + System.out.println("Add to pending down jobs"); + downPendingJobs.add(request); + } + } + +} + +enum State { + + MOVING, STOPPED, IDLE + +} + +enum Direction { + + UP, DOWN + +} + +class Request implements Comparable { + private InternalRequest internalRequest; + private ExternalRequest externalRequest; + + public Request(InternalRequest internalRequest, ExternalRequest externalRequest) { + this.internalRequest = internalRequest; + this.externalRequest = externalRequest; + } + + public InternalRequest getInternalRequest() { + return internalRequest; + } + + public void setInternalRequest(InternalRequest internalRequest) { + this.internalRequest = internalRequest; + } + + public ExternalRequest getExternalRequest() { + return externalRequest; + } + + public void setExternalRequest(ExternalRequest externalRequest) { + this.externalRequest = externalRequest; + } + + @Override + public int compareTo(Request req) { + if (this.getInternalRequest().getDestinationFloor() == req.getInternalRequest().getDestinationFloor()) + return 0; + else if (this.getInternalRequest().getDestinationFloor() > req.getInternalRequest().getDestinationFloor()) + return 1; + else + return -1; + } + +} + +class ProcessJobWorker implements Runnable { + + private Elevator elevator; + + ProcessJobWorker(Elevator elevator) { + this.elevator = elevator; + } + + @Override + public void run() { + /** + * start the elevator + */ + elevator.startElevator(); + } + +} + +class AddJobWorker implements Runnable { + + private Elevator elevator; + private Request request; + + AddJobWorker(Elevator elevator, Request request) { + this.elevator = elevator; + this.request = request; + } + + @Override + public void run() { + + try { + Thread.sleep(200); + } catch (InterruptedException e) { + + e.printStackTrace(); + } + elevator.addJob(request); + } + +} + +class ExternalRequest { + + private Direction directionToGo; + private int sourceFloor; + + public ExternalRequest(Direction directionToGo, int sourceFloor) { + this.directionToGo = directionToGo; + this.sourceFloor = sourceFloor; + } + + public Direction getDirectionToGo() { + return directionToGo; + } + + public void setDirectionToGo(Direction directionToGo) { + this.directionToGo = directionToGo; + } + + public int getSourceFloor() { + return sourceFloor; + } + + public void setSourceFloor(int sourceFloor) { + this.sourceFloor = sourceFloor; + } + + @Override + public String toString() { + return " The Elevator has been requested on floor - " + sourceFloor + " and the person wants go in the - " + + directionToGo; + } + +} + +class InternalRequest { + private int destinationFloor; + + public InternalRequest(int destinationFloor) { + this.destinationFloor = destinationFloor; + } + + public int getDestinationFloor() { + return destinationFloor; + } + + public void setDestinationFloor(int destinationFloor) { + this.destinationFloor = destinationFloor; + } + + @Override + public String toString() { + return "The destinationFloor is - " + destinationFloor; + } + +} + +public class TestElevator { + + public static void main(String args[]) { + + Elevator elevator = new Elevator(); + + /** + * Thread for starting the elevator + */ + ProcessJobWorker processJobWorker = new ProcessJobWorker(elevator); + Thread t2 = new Thread(processJobWorker); + t2.start(); + + try { + Thread.sleep(3000); + } catch (InterruptedException e) { + + e.printStackTrace(); + } + + ExternalRequest er = new ExternalRequest(Direction.UP, 0); + + InternalRequest ir = new InternalRequest(5); + + Request request1 = new Request(ir, er); + + + /** + * Pass job to the elevator + */ + new Thread(new AddJobWorker(elevator, request1)).start(); + + try { + Thread.sleep(3000); + } catch (InterruptedException e) { + + e.printStackTrace(); + } + + + } + +} \ No newline at end of file diff --git a/src/main/java/lld/vendingmachine/HasMoneyState.java b/src/main/java/lld/vendingmachine/HasMoneyState.java new file mode 100644 index 0000000..3d9596c --- /dev/null +++ b/src/main/java/lld/vendingmachine/HasMoneyState.java @@ -0,0 +1,34 @@ +package lld.vendingmachine; + +public class HasMoneyState implements VendingMachineState { + private VendingMachine vendingMachine; + + public HasMoneyState(VendingMachine vendingMachine) { this.vendingMachine = vendingMachine; } + + @Override + public void insertMoney() { + System.out.println("\tMoney is Inserted."); + } + + @Override + public void ejectMoney() { + System.out.println("\tRejecting Money."); + vendingMachine.setState(vendingMachine.getNoMoneyState()); + } + + @Override + public void selectProduct() { + System.out.println("\tItem is Selected."); + vendingMachine.setState(vendingMachine.getSoldState()); + } + + @Override + public void dispenseProduct() { + System.out.println("\tDispensing Product."); + } + + @Override + public String toString() { + return "\tMoney Accepted State, Waiting for Product Selection."; + } +} \ No newline at end of file diff --git a/src/main/java/lld/vendingmachine/Main.java b/src/main/java/lld/vendingmachine/Main.java new file mode 100644 index 0000000..f9ba696 --- /dev/null +++ b/src/main/java/lld/vendingmachine/Main.java @@ -0,0 +1,50 @@ +package lld.vendingmachine; + +public class Main { + public static void main(String[] args) { + System.out.println("State Design Pattern Vending Machine."); + System.out.println("Vending Machine activated by Machine States (HasMoney, NoMoney, Sold, SoldOut).\n"); + + VendingMachine vendingMachine = new VendingMachine(2); + System.out.println(vendingMachine); + System.out.println(); + + vendingMachine.insertMoney(); + System.out.println(vendingMachine); + System.out.println(); + + vendingMachine.selectProduct(); + System.out.println(vendingMachine); + System.out.println(); + + vendingMachine.dispenseProduct(); + System.out.println(vendingMachine); + System.out.println(); + + + vendingMachine.insertMoney(); + System.out.println(vendingMachine); + System.out.println(); + + vendingMachine.selectProduct(); + System.out.println(vendingMachine); + System.out.println(); + + vendingMachine.dispenseProduct(); + System.out.println(vendingMachine); + System.out.println(); + + + vendingMachine.insertMoney(); + System.out.println(vendingMachine); + System.out.println(); + + vendingMachine.selectProduct(); + System.out.println(vendingMachine); + System.out.println(); + + vendingMachine.dispenseProduct(); + System.out.println(vendingMachine); + System.out.println(); + } +} \ No newline at end of file diff --git a/src/main/java/lld/vendingmachine/NoMoneyState.java b/src/main/java/lld/vendingmachine/NoMoneyState.java new file mode 100644 index 0000000..8dbac65 --- /dev/null +++ b/src/main/java/lld/vendingmachine/NoMoneyState.java @@ -0,0 +1,35 @@ +package lld.vendingmachine; + +public class NoMoneyState implements VendingMachineState { + private VendingMachine vendingMachine; + + public NoMoneyState(VendingMachine vendingMachine) { this.vendingMachine = vendingMachine; } + + @Override + public void insertMoney() { + System.out.println("\tMoney is Inserted."); + vendingMachine.setState(vendingMachine.getHasMoneyState()); + } + + @Override + public void ejectMoney() { + System.out.println("\tNo Money."); + vendingMachine.setState(vendingMachine.getNoMoneyState()); + } + + @Override + public void selectProduct() { + System.out.println("\tSelection Made. No Money."); + + } + + @Override + public void dispenseProduct() { + System.out.println("\tNo Disperse before Money Insertion."); + } + + @Override + public String toString() { + return "\tNo Money State, Waiting for Money."; + } +} \ No newline at end of file diff --git a/src/main/java/lld/vendingmachine/SoldOutState.java b/src/main/java/lld/vendingmachine/SoldOutState.java new file mode 100644 index 0000000..496afcc --- /dev/null +++ b/src/main/java/lld/vendingmachine/SoldOutState.java @@ -0,0 +1,33 @@ +package lld.vendingmachine; + +public class SoldOutState implements VendingMachineState { + @SuppressWarnings("unused") + private VendingMachine vendingMachine; + + public SoldOutState(VendingMachine vendingMachine) { this.vendingMachine = vendingMachine; } + + @Override + public void insertMoney() { + System.out.println("\tProducts Sold Out"); + } + + @Override + public void ejectMoney() { + System.out.println("\tMoney is Rejected Back to Customer."); + } + + @Override + public void selectProduct() { + System.out.println("\tProduct Not Available."); + } + + @Override + public void dispenseProduct() { + System.out.println("\tProduct Sold Out."); + } + + @Override + public String toString() { + return "\tProduct Sold Out State."; + } +} \ No newline at end of file diff --git a/src/main/java/lld/vendingmachine/SoldState.java b/src/main/java/lld/vendingmachine/SoldState.java new file mode 100644 index 0000000..fc8bb61 --- /dev/null +++ b/src/main/java/lld/vendingmachine/SoldState.java @@ -0,0 +1,43 @@ +package lld.vendingmachine; + +public class SoldState implements VendingMachineState { + private VendingMachine vendingMachine; + + public SoldState(VendingMachine vendingMachine) { this.vendingMachine = vendingMachine; } + + @Override + public void insertMoney() { + System.out.println("\tProduct is Ready."); + } + + @Override + public void ejectMoney() { + System.out.println("\tProduct is is Already Coming Out."); + + } + + @Override + public void selectProduct() { + System.out.println("\tProduct is Already Selected."); + } + + @Override + public void dispenseProduct() { + + if(vendingMachine.getNumberOfProducts() > 0) { + System.out.println("\tProduct is Being Dispensed."); + vendingMachine.setNumberOfProducts(vendingMachine.getNumberOfProducts()-1); + vendingMachine.setState(vendingMachine.getNoMoneyState()); + } + else { + System.out.println("\tOut of Products."); + vendingMachine.setState(vendingMachine.getSoldOutState()); + vendingMachine.ejectMoney(); + } + } + + @Override + public String toString() { + return "\tDispensing Product State."; + } +} \ No newline at end of file diff --git a/src/main/java/lld/vendingmachine/State.java b/src/main/java/lld/vendingmachine/State.java new file mode 100644 index 0000000..863da44 --- /dev/null +++ b/src/main/java/lld/vendingmachine/State.java @@ -0,0 +1,8 @@ +package lld.vendingmachine; + +public interface State { + public void collectCash(int cash); + public void dispenseChange(String productCode); + public void dispenseItem(String productCode); + public void cancelTransaction(); +} \ No newline at end of file diff --git a/src/main/java/lld/vendingmachine/VendingMachine.java b/src/main/java/lld/vendingmachine/VendingMachine.java new file mode 100644 index 0000000..e600974 --- /dev/null +++ b/src/main/java/lld/vendingmachine/VendingMachine.java @@ -0,0 +1,87 @@ +package lld.vendingmachine; + +public class VendingMachine { + private VendingMachineState hasMoneyState; + private VendingMachineState noMoneyState; + private VendingMachineState soldOutState; + private VendingMachineState soldState; + private VendingMachineState machineState; + + private int numberOfProducts; + + //Vending Machine Constructor: + public VendingMachine(int numberOfProducts) { + this.numberOfProducts = numberOfProducts; + hasMoneyState = new HasMoneyState(this); + noMoneyState = new NoMoneyState(this); + soldOutState = new SoldOutState(this); + soldState = new SoldState(this); + if (numberOfProducts > 0) { + machineState = noMoneyState; + } + } + + //Getters and Setters: + public int getNumberOfProducts() { + return numberOfProducts; + } + + public void setNumberOfProducts(int numberOfProducts) { + this.numberOfProducts = numberOfProducts; + } + + public VendingMachineState getHasMoneyState() { + return hasMoneyState; + } + + public VendingMachineState getNoMoneyState() { + return noMoneyState; + } + + public VendingMachineState getSoldOutState() { + return soldOutState; + } + + public VendingMachineState getSoldState() { + return soldState; + } + + public VendingMachineState getMachineState() { + return machineState; + } + + public void setState(VendingMachineState state) { + machineState = state; + } + + //Vending Machine Actions: + public void insertMoney() { + machineState.insertMoney(); + } + + public void ejectMoney() { + machineState.ejectMoney(); + } + + public void selectProduct() { + machineState.selectProduct(); + } + + public void dispenseProduct() { + machineState.dispenseProduct(); + } + + @Override + public String toString() { + StringBuffer result = new StringBuffer(); + result.append("Welcome to Vending Machine.\n"); + result.append("Inventory:\t" + numberOfProducts + " product"); + if (numberOfProducts != 1) { + result.append("s.\n"); + } else { + result.append(".\n"); + } + result.append("Vending Machine State:\t" + machineState + "\n"); + return result.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/lld/vendingmachine/VendingMachineState.java b/src/main/java/lld/vendingmachine/VendingMachineState.java new file mode 100644 index 0000000..487518e --- /dev/null +++ b/src/main/java/lld/vendingmachine/VendingMachineState.java @@ -0,0 +1,11 @@ +package lld.vendingmachine; + +public interface VendingMachineState { + public void insertMoney(); + + public void ejectMoney(); + + public void selectProduct(); + + public void dispenseProduct(); +} \ No newline at end of file diff --git a/src/main/java/machinecoding/snakesandladder/DiceService.java b/src/main/java/machinecoding/snakesandladder/DiceService.java new file mode 100644 index 0000000..62016cf --- /dev/null +++ b/src/main/java/machinecoding/snakesandladder/DiceService.java @@ -0,0 +1,9 @@ +package machinecoding.snakesandladder; + +import java.util.Random; + +public class DiceService { + public static int roll() { + return new Random().nextInt(6) + 1; // The game will have a six sided dice numbered from 1 to 6 and will always give a random number on rolling it. + } +} \ No newline at end of file diff --git a/src/main/java/machinecoding/snakesandladder/Driver.java b/src/main/java/machinecoding/snakesandladder/Driver.java new file mode 100644 index 0000000..4d5cac5 --- /dev/null +++ b/src/main/java/machinecoding/snakesandladder/Driver.java @@ -0,0 +1,36 @@ +package machinecoding.snakesandladder; + +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; + +public class Driver { + public static void main(String[] args) { + Scanner scanner = new Scanner(System.in); + + int noOfSnakes = scanner.nextInt(); + List snakes = new ArrayList(); + for (int i = 0; i < noOfSnakes; i++) { + snakes.add(new Snake(scanner.nextInt(), scanner.nextInt())); + } + + int noOfLadders = scanner.nextInt(); + List ladders = new ArrayList(); + for (int i = 0; i < noOfLadders; i++) { + ladders.add(new Ladder(scanner.nextInt(), scanner.nextInt())); + } + + int noOfPlayers = scanner.nextInt(); + List players = new ArrayList(); + for (int i = 0; i < noOfPlayers; i++) { + players.add(new Player(scanner.next())); + } + + SnakeAndLadderService snakeAndLadderService = new SnakeAndLadderService(); + snakeAndLadderService.setPlayers(players); + snakeAndLadderService.setSnakes(snakes); + snakeAndLadderService.setLadders(ladders); + + snakeAndLadderService.startGame(); + } +} \ No newline at end of file diff --git a/src/main/java/machinecoding/snakesandladder/Ladder.java b/src/main/java/machinecoding/snakesandladder/Ladder.java new file mode 100644 index 0000000..e5dc400 --- /dev/null +++ b/src/main/java/machinecoding/snakesandladder/Ladder.java @@ -0,0 +1,20 @@ +package machinecoding.snakesandladder; + +public class Ladder { + // Each ladder will have its start position at some number and end position at a larger number. + private int start; + private int end; + + public Ladder(int start, int end) { + this.start = start; + this.end = end; + } + + public int getStart() { + return start; + } + + public int getEnd() { + return end; + } +} \ No newline at end of file diff --git a/src/main/java/machinecoding/snakesandladder/Player.java b/src/main/java/machinecoding/snakesandladder/Player.java new file mode 100644 index 0000000..11f8f31 --- /dev/null +++ b/src/main/java/machinecoding/snakesandladder/Player.java @@ -0,0 +1,21 @@ +package machinecoding.snakesandladder; + +import java.util.UUID; + +public class Player { + private String name; + private String id; + + public Player(String name) { + this.name = name; + this.id = UUID.randomUUID().toString(); + } + + public String getName() { + return name; + } + + public String getId() { + return id; + } +} \ No newline at end of file diff --git a/src/main/java/machinecoding/snakesandladder/Snake.java b/src/main/java/machinecoding/snakesandladder/Snake.java new file mode 100644 index 0000000..ddbd515 --- /dev/null +++ b/src/main/java/machinecoding/snakesandladder/Snake.java @@ -0,0 +1,20 @@ +package machinecoding.snakesandladder; + +public class Snake { + // Each snake will have its head at some number and its tail at a smaller number. + private int start; + private int end; + + public Snake(int start, int end) { + this.start = start; + this.end = end; + } + + public int getStart() { + return start; + } + + public int getEnd() { + return end; + } +} \ No newline at end of file diff --git a/src/main/java/machinecoding/snakesandladder/SnakeAndLadderBoard.java b/src/main/java/machinecoding/snakesandladder/SnakeAndLadderBoard.java new file mode 100644 index 0000000..b0fdf7f --- /dev/null +++ b/src/main/java/machinecoding/snakesandladder/SnakeAndLadderBoard.java @@ -0,0 +1,48 @@ +package machinecoding.snakesandladder; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class SnakeAndLadderBoard { + private int size; + private List snakes; // The board also contains some snakes and ladders. + private List ladders; + private Map playerPieces; + + public SnakeAndLadderBoard(int size) { + this.size = size; + this.snakes = new ArrayList(); + this.ladders = new ArrayList(); + this.playerPieces = new HashMap(); + } + + public int getSize() { + return size; + } + + public List getSnakes() { + return snakes; + } + + public void setSnakes(List snakes) { + this.snakes = snakes; + } + + public List getLadders() { + return ladders; + } + + public void setLadders(List ladders) { + this.ladders = ladders; + } + + public Map getPlayerPieces() { + return playerPieces; + } + + public void setPlayerPieces(Map playerPieces) { + this.playerPieces = playerPieces; + } +} \ No newline at end of file diff --git a/src/main/java/machinecoding/snakesandladder/SnakeAndLadderService.java b/src/main/java/machinecoding/snakesandladder/SnakeAndLadderService.java new file mode 100644 index 0000000..ec54192 --- /dev/null +++ b/src/main/java/machinecoding/snakesandladder/SnakeAndLadderService.java @@ -0,0 +1,148 @@ +package machinecoding.snakesandladder; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; + +public class SnakeAndLadderService { + private SnakeAndLadderBoard snakeAndLadderBoard; + private int initialNumberOfPlayers; + private Queue players; // Comment: Keeping players in game service as they are specific to this game and not the board. Keeping pieces in the board instead. + private boolean isGameCompleted; + + private int noOfDices; //Optional Rule 1 + private boolean shouldGameContinueTillLastPlayer; //Optional Rule 3 + private boolean shouldAllowMultipleDiceRollOnSix; //Optional Rule 4 + + private static final int DEFAULT_BOARD_SIZE = 100; //The board will have 100 cells numbered from 1 to 100. + private static final int DEFAULT_NO_OF_DICES = 1; + + public SnakeAndLadderService(int boardSize) { + this.snakeAndLadderBoard = new SnakeAndLadderBoard(boardSize); //Optional Rule 2 + this.players = new LinkedList<>(); + this.noOfDices = SnakeAndLadderService.DEFAULT_NO_OF_DICES; + } + + public SnakeAndLadderService() { + this(SnakeAndLadderService.DEFAULT_BOARD_SIZE); + } + + /** + * ====Setters for making the game more extensible==== + */ + + public void setNoOfDices(int noOfDices) { + this.noOfDices = noOfDices; + } + + public void setShouldGameContinueTillLastPlayer(boolean shouldGameContinueTillLastPlayer) { + this.shouldGameContinueTillLastPlayer = shouldGameContinueTillLastPlayer; + } + + public void setShouldAllowMultipleDiceRollOnSix(boolean shouldAllowMultipleDiceRollOnSix) { + this.shouldAllowMultipleDiceRollOnSix = shouldAllowMultipleDiceRollOnSix; + } + + /** + * ==================Initialize board================== + */ + + public void setPlayers(List players) { + this.players = new LinkedList(); + this.initialNumberOfPlayers = players.size(); + Map playerPieces = new HashMap(); + for (Player player : players) { + this.players.add(player); + playerPieces.put(player.getId(), 0); //Each player has a piece which is initially kept outside the board (i.e., at position 0). + } + snakeAndLadderBoard.setPlayerPieces(playerPieces); // Add pieces to board + } + + public void setSnakes(List snakes) { + snakeAndLadderBoard.setSnakes(snakes); // Add snakes to board + } + + public void setLadders(List ladders) { + snakeAndLadderBoard.setLadders(ladders); // Add ladders to board + } + + /** + * ==========Core business logic for the game========== + */ + + private int getNewPositionAfterGoingThroughSnakesAndLadders(int newPosition) { + int previousPosition; + + do { + previousPosition = newPosition; + for (Snake snake : snakeAndLadderBoard.getSnakes()) { + if (snake.getStart() == newPosition) { + newPosition = snake.getEnd(); // Whenever a piece ends up at a position with the head of the snake, the piece should go down to the position of the tail of that snake. + } + } + + for (Ladder ladder : snakeAndLadderBoard.getLadders()) { + if (ladder.getStart() == newPosition) { + newPosition = ladder.getEnd(); // Whenever a piece ends up at a position with the start of the ladder, the piece should go up to the position of the end of that ladder. + } + } + } while (newPosition != previousPosition); // There could be another snake/ladder at the tail of the snake or the end position of the ladder and the piece should go up/down accordingly. + return newPosition; + } + + private void movePlayer(Player player, int positions) { + int oldPosition = snakeAndLadderBoard.getPlayerPieces().get(player.getId()); + int newPosition = oldPosition + positions; // Based on the dice value, the player moves their piece forward that number of cells. + + int boardSize = snakeAndLadderBoard.getSize(); + + // Can modify this logic to handle side case when there are multiple dices (Optional requirements) + if (newPosition > boardSize) { + newPosition = oldPosition; // After the dice roll, if a piece is supposed to move outside position 100, it does not move. + } else { + newPosition = getNewPositionAfterGoingThroughSnakesAndLadders(newPosition); + } + + snakeAndLadderBoard.getPlayerPieces().put(player.getId(), newPosition); + + System.out.println(player.getName() + " rolled a " + positions + " and moved from " + oldPosition +" to " + newPosition); + } + + private int getTotalValueAfterDiceRolls() { + // Can use noOfDices and setShouldAllowMultipleDiceRollOnSix here to get total value (Optional requirements) + return DiceService.roll(); + } + + private boolean hasPlayerWon(Player player) { + // Can change the logic a bit to handle special cases when there are more than one dice (Optional requirements) + int playerPosition = snakeAndLadderBoard.getPlayerPieces().get(player.getId()); + int winningPosition = snakeAndLadderBoard.getSize(); + return playerPosition == winningPosition; // A player wins if it exactly reaches the position 100 and the game ends there. + } + + private boolean isGameCompleted() { + // Can use shouldGameContinueTillLastPlayer to change the logic of determining if game is completed (Optional requirements) + int currentNumberOfPlayers = players.size(); + return currentNumberOfPlayers < initialNumberOfPlayers; + } + + public void startGame() { + while (!isGameCompleted()) { + int totalDiceValue = getTotalValueAfterDiceRolls(); // Each player rolls the dice when their turn comes. + Player currentPlayer = players.poll(); + movePlayer(currentPlayer, totalDiceValue); + if (hasPlayerWon(currentPlayer)) { + System.out.println(currentPlayer.getName() + " wins the game"); + snakeAndLadderBoard.getPlayerPieces().remove(currentPlayer.getId()); + } else { + players.add(currentPlayer); + } + } + } + + /** + * ======================================================= + */ +} \ No newline at end of file diff --git a/src/main/java/machinecoding/splitwise/Driver.java b/src/main/java/machinecoding/splitwise/Driver.java new file mode 100644 index 0000000..0bf0206 --- /dev/null +++ b/src/main/java/machinecoding/splitwise/Driver.java @@ -0,0 +1,64 @@ +package machinecoding.splitwise; + +import java.util.ArrayList; +import java.util.List; +import java.util.Scanner; + +public class Driver { + public static void main(String[] args) { + ExpenseManager expenseManager = new ExpenseManager(); + + expenseManager.addUser(new User("u1", "User1", "gaurav@workat.tech", "9876543210")); + expenseManager.addUser(new User("u2", "User2", "sagar@workat.tech", "9876543210")); + expenseManager.addUser(new User("u3", "User3", "hi@workat.tech", "9876543210")); + expenseManager.addUser(new User("u4", "User4", "mock-interviews@workat.tech", "9876543210")); + + Scanner scanner = new Scanner(System.in); + while (true) { + String command = scanner.nextLine(); + String[] commands = command.split(" "); + String commandType = commands[0]; + + switch (commandType) { + case "SHOW": + if (commands.length == 1) { + expenseManager.showBalances(); + } else { + expenseManager.showBalance(commands[1]); + } + break; + case "EXPENSE": + //EXPENSE + String paidBy = commands[1]; + Double amount = Double.parseDouble(commands[2]); + int noOfUsers = Integer.parseInt(commands[3]); + String expenseType = commands[4 + noOfUsers]; + List splits = new ArrayList<>(); + switch (expenseType) { + case "EQUAL": + for (int i = 0; i < noOfUsers; i++) { + splits.add(new EqualSplit(expenseManager.userMap.get(commands[4 + i]))); + } + expenseManager.addExpense(ExpenseType.EQUAL, amount, paidBy, splits, null); + break; + case "EXACT": + for (int i = 0; i < noOfUsers; i++) { + splits.add(new ExactSplit(expenseManager.userMap.get(commands[4 + i]), Double.parseDouble(commands[5 + noOfUsers + i]))); + } + expenseManager.addExpense(ExpenseType.EXACT, amount, paidBy, splits, null); + break; + case "PERCENT": + for (int i = 0; i < noOfUsers; i++) { + splits.add(new PercentSplit(expenseManager.userMap.get(commands[4 + i]), Double.parseDouble(commands[5 + noOfUsers + i]))); + } + expenseManager.addExpense(ExpenseType.PERCENT, amount, paidBy, splits, null); + break; + } + break; + + default: + break; + } + } + } +} \ No newline at end of file diff --git a/src/main/java/machinecoding/splitwise/EqualExpense.java b/src/main/java/machinecoding/splitwise/EqualExpense.java new file mode 100644 index 0000000..05c1339 --- /dev/null +++ b/src/main/java/machinecoding/splitwise/EqualExpense.java @@ -0,0 +1,20 @@ +package machinecoding.splitwise; + +import java.util.List; + +public class EqualExpense extends Expense { + public EqualExpense(double amount, User paidBy, List splits, ExpenseMetadata expenseMetadata) { + super(amount, paidBy, splits, expenseMetadata); + } + + @Override + public boolean validate() { + for (Split split : getSplits()) { + if (!(split instanceof EqualSplit)) { + return false; + } + } + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/machinecoding/splitwise/EqualSplit.java b/src/main/java/machinecoding/splitwise/EqualSplit.java new file mode 100644 index 0000000..e60b8e2 --- /dev/null +++ b/src/main/java/machinecoding/splitwise/EqualSplit.java @@ -0,0 +1,8 @@ +package machinecoding.splitwise; + +public class EqualSplit extends Split { + + public EqualSplit(User user) { + super(user); + } +} \ No newline at end of file diff --git a/src/main/java/machinecoding/splitwise/ExactExpense.java b/src/main/java/machinecoding/splitwise/ExactExpense.java new file mode 100644 index 0000000..dd92066 --- /dev/null +++ b/src/main/java/machinecoding/splitwise/ExactExpense.java @@ -0,0 +1,31 @@ +package machinecoding.splitwise; + +import java.util.List; + +public class ExactExpense extends Expense { + public ExactExpense(double amount, User paidBy, List splits, ExpenseMetadata expenseMetadata) { + super(amount, paidBy, splits, expenseMetadata); + } + + @Override + public boolean validate() { + for (Split split : getSplits()) { + if (!(split instanceof ExactSplit)) { + return false; + } + } + + double totalAmount = getAmount(); + double sumSplitAmount = 0; + for (Split split : getSplits()) { + ExactSplit exactSplit = (ExactSplit) split; + sumSplitAmount += exactSplit.getAmount(); + } + + if (totalAmount != sumSplitAmount) { + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/machinecoding/splitwise/ExactSplit.java b/src/main/java/machinecoding/splitwise/ExactSplit.java new file mode 100644 index 0000000..60b20c0 --- /dev/null +++ b/src/main/java/machinecoding/splitwise/ExactSplit.java @@ -0,0 +1,9 @@ +package machinecoding.splitwise; + +public class ExactSplit extends Split { + + public ExactSplit(User user, double amount) { + super(user); + this.amount = amount; + } +} \ No newline at end of file diff --git a/src/main/java/machinecoding/splitwise/Expense.java b/src/main/java/machinecoding/splitwise/Expense.java new file mode 100644 index 0000000..34e10e2 --- /dev/null +++ b/src/main/java/machinecoding/splitwise/Expense.java @@ -0,0 +1,60 @@ +package machinecoding.splitwise; + +import java.util.List; + +public abstract class Expense { + private String id; + private double amount; + private User paidBy; + private List splits; + private ExpenseMetadata metadata; + + public Expense(double amount, User paidBy, List splits, ExpenseMetadata metadata) { + this.amount = amount; + this.paidBy = paidBy; + this.splits = splits; + this.metadata = metadata; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public double getAmount() { + return amount; + } + + public void setAmount(double amount) { + this.amount = amount; + } + + public User getPaidBy() { + return paidBy; + } + + public void setPaidBy(User paidBy) { + this.paidBy = paidBy; + } + + public List getSplits() { + return splits; + } + + public void setSplits(List splits) { + this.splits = splits; + } + + public ExpenseMetadata getMetadata() { + return metadata; + } + + public void setMetadata(ExpenseMetadata metadata) { + this.metadata = metadata; + } + + public abstract boolean validate(); +} \ No newline at end of file diff --git a/src/main/java/machinecoding/splitwise/ExpenseManager.java b/src/main/java/machinecoding/splitwise/ExpenseManager.java new file mode 100644 index 0000000..c729e33 --- /dev/null +++ b/src/main/java/machinecoding/splitwise/ExpenseManager.java @@ -0,0 +1,78 @@ +package machinecoding.splitwise; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class ExpenseManager { + List expenses; + Map userMap; + Map> balanceSheet; + + public ExpenseManager() { + expenses = new ArrayList<>(); + userMap = new HashMap<>(); + balanceSheet = new HashMap<>(); + } + + public void addUser(User user) { + userMap.put(user.getId(), user); + balanceSheet.put(user.getId(), new HashMap<>()); + } + + public void addExpense(ExpenseType expenseType, double amount, String paidBy, List splits, ExpenseMetadata expenseMetadata) { + Expense expense = ExpenseService.createExpense(expenseType, amount, userMap.get(paidBy), splits, expenseMetadata); + expenses.add(expense); + for (Split split : expense.getSplits()) { + String paidTo = split.getUser().getId(); + Map balances = balanceSheet.get(paidBy); + balances.putIfAbsent(paidTo, 0.0); + balances.put(paidTo, balances.get(paidTo) + split.getAmount()); + + balances = balanceSheet.get(paidTo); + balances.putIfAbsent(paidBy, 0.0); + balances.put(paidBy, balances.get(paidBy) - split.getAmount()); + } + } + + public void showBalance(String userId) { + boolean isEmpty = true; + for (Map.Entry userBalance : balanceSheet.get(userId).entrySet()) { + if (userBalance.getValue() != 0) { + isEmpty = false; + printBalance(userId, userBalance.getKey(), userBalance.getValue()); + } + } + + if (isEmpty) { + System.out.println("No balances"); + } + } + + public void showBalances() { + boolean isEmpty = true; + for (Map.Entry> allBalances : balanceSheet.entrySet()) { + for (Map.Entry userBalance : allBalances.getValue().entrySet()) { + if (userBalance.getValue() > 0) { + isEmpty = false; + printBalance(allBalances.getKey(), userBalance.getKey(), userBalance.getValue()); + } + } + } + + if (isEmpty) { + System.out.println("No balances"); + } + } + + private void printBalance(String user1, String user2, double amount) { + String user1Name = userMap.get(user1).getName(); + String user2Name = userMap.get(user2).getName(); + if (amount < 0) { + System.out.println(user1Name + " owes " + user2Name + ": " + Math.abs(amount)); + } else if (amount > 0) { + System.out.println(user2Name + " owes " + user1Name + ": " + Math.abs(amount)); + } + } +} \ No newline at end of file diff --git a/src/main/java/machinecoding/splitwise/ExpenseMetadata.java b/src/main/java/machinecoding/splitwise/ExpenseMetadata.java new file mode 100644 index 0000000..29ba868 --- /dev/null +++ b/src/main/java/machinecoding/splitwise/ExpenseMetadata.java @@ -0,0 +1,37 @@ +package machinecoding.splitwise; + +public class ExpenseMetadata { + private String name; + private String imgUrl; + private String notes; + + public ExpenseMetadata(String name, String imgUrl, String notes) { + this.name = name; + this.imgUrl = imgUrl; + this.notes = notes; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getImgUrl() { + return imgUrl; + } + + public void setImgUrl(String imgUrl) { + this.imgUrl = imgUrl; + } + + public String getNotes() { + return notes; + } + + public void setNotes(String notes) { + this.notes = notes; + } +} \ No newline at end of file diff --git a/src/main/java/machinecoding/splitwise/ExpenseService.java b/src/main/java/machinecoding/splitwise/ExpenseService.java new file mode 100644 index 0000000..79c75ba --- /dev/null +++ b/src/main/java/machinecoding/splitwise/ExpenseService.java @@ -0,0 +1,28 @@ +package machinecoding.splitwise; + +import java.util.List; + +public class ExpenseService { + public static Expense createExpense(ExpenseType expenseType, double amount, User paidBy, List splits, ExpenseMetadata expenseMetadata) { + switch (expenseType) { + case EXACT: + return new ExactExpense(amount, paidBy, splits, expenseMetadata); + case PERCENT: + for (Split split : splits) { + PercentSplit percentSplit = (PercentSplit) split; + split.setAmount((amount*percentSplit.getPercent())/100.0); + } + return new PercentExpense(amount, paidBy, splits, expenseMetadata); + case EQUAL: + int totalSplits = splits.size(); + double splitAmount = ((double) Math.round(amount*100/totalSplits))/100.0; + for (Split split : splits) { + split.setAmount(splitAmount); + } + splits.get(0).setAmount(splitAmount + (amount - splitAmount*totalSplits)); + return new EqualExpense(amount, paidBy, splits, expenseMetadata); + default: + return null; + } + } +} \ No newline at end of file diff --git a/src/main/java/machinecoding/splitwise/ExpenseType.java b/src/main/java/machinecoding/splitwise/ExpenseType.java new file mode 100644 index 0000000..837bbc0 --- /dev/null +++ b/src/main/java/machinecoding/splitwise/ExpenseType.java @@ -0,0 +1,7 @@ +package machinecoding.splitwise; + +public enum ExpenseType { + EQUAL, + EXACT, + PERCENT +} \ No newline at end of file diff --git a/src/main/java/machinecoding/splitwise/PercentExpense.java b/src/main/java/machinecoding/splitwise/PercentExpense.java new file mode 100644 index 0000000..f2e1012 --- /dev/null +++ b/src/main/java/machinecoding/splitwise/PercentExpense.java @@ -0,0 +1,27 @@ +package machinecoding.splitwise; + +import java.util.List; + +public class PercentExpense extends Expense { + public PercentExpense(double amount, User paidBy, List splits, ExpenseMetadata expenseMetadata) { + super(amount, paidBy, splits, expenseMetadata); + } + + @Override + public boolean validate() { + for (Split split : getSplits()) { + if (!(split instanceof PercentSplit)) { + return false; + } + } + + double totalPercent = 100; + double sumSplitPercent = 0; + for (Split split : getSplits()) { + PercentSplit exactSplit = (PercentSplit) split; + sumSplitPercent += exactSplit.getPercent(); + } + + return totalPercent == sumSplitPercent; + } +} \ No newline at end of file diff --git a/src/main/java/machinecoding/splitwise/PercentSplit.java b/src/main/java/machinecoding/splitwise/PercentSplit.java new file mode 100644 index 0000000..da65eb2 --- /dev/null +++ b/src/main/java/machinecoding/splitwise/PercentSplit.java @@ -0,0 +1,18 @@ +package machinecoding.splitwise; + +public class PercentSplit extends Split { + double percent; + + public PercentSplit(User user, double percent) { + super(user); + this.percent = percent; + } + + public double getPercent() { + return percent; + } + + public void setPercent(double percent) { + this.percent = percent; + } +} \ No newline at end of file diff --git a/src/main/java/machinecoding/splitwise/Split.java b/src/main/java/machinecoding/splitwise/Split.java new file mode 100644 index 0000000..239d5c4 --- /dev/null +++ b/src/main/java/machinecoding/splitwise/Split.java @@ -0,0 +1,26 @@ +package machinecoding.splitwise; + +public abstract class Split { + private User user; + double amount; + + public Split(User user) { + this.user = user; + } + + public User getUser() { + return user; + } + + public void setUser(User user) { + this.user = user; + } + + public double getAmount() { + return amount; + } + + public void setAmount(double amount) { + this.amount = amount; + } +} \ No newline at end of file diff --git a/src/main/java/machinecoding/splitwise/User.java b/src/main/java/machinecoding/splitwise/User.java new file mode 100644 index 0000000..6c28510 --- /dev/null +++ b/src/main/java/machinecoding/splitwise/User.java @@ -0,0 +1,47 @@ +package machinecoding.splitwise; + +public class User { + private String id; + private String name; + private String email; + private String phone; + + public User(String id, String name, String email, String phone) { + this.id = id; + this.name = name; + this.email = email; + this.phone = phone; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getPhone() { + return phone; + } + + public void setPhone(String phone) { + this.phone = phone; + } +} \ No newline at end of file diff --git a/src/main/java/machinecoding/uditagarwal/chess/Main.java b/src/main/java/machinecoding/uditagarwal/chess/Main.java new file mode 100644 index 0000000..e6aa31c --- /dev/null +++ b/src/main/java/machinecoding/uditagarwal/chess/Main.java @@ -0,0 +1,8 @@ +package machinecoding.uditagarwal.chess; + +public class Main { + + public static void main(String args[]) { + + } +} diff --git a/src/main/java/machinecoding/uditagarwal/chess/conditions/MoveBaseCondition.java b/src/main/java/machinecoding/uditagarwal/chess/conditions/MoveBaseCondition.java new file mode 100644 index 0000000..156a2e7 --- /dev/null +++ b/src/main/java/machinecoding/uditagarwal/chess/conditions/MoveBaseCondition.java @@ -0,0 +1,12 @@ +package machinecoding.uditagarwal.chess.conditions; + + +import machinecoding.uditagarwal.chess.model.Piece; + +/** + * It provides the base condition for a piece to make a move. The piece would only be allowed to move from its current + * position if the condition fulfills. + */ +public interface MoveBaseCondition { + boolean isBaseConditionFullfilled(Piece piece); +} diff --git a/src/main/java/machinecoding/uditagarwal/chess/conditions/MoveBaseConditionFirstMove.java b/src/main/java/machinecoding/uditagarwal/chess/conditions/MoveBaseConditionFirstMove.java new file mode 100644 index 0000000..c93e854 --- /dev/null +++ b/src/main/java/machinecoding/uditagarwal/chess/conditions/MoveBaseConditionFirstMove.java @@ -0,0 +1,14 @@ +package machinecoding.uditagarwal.chess.conditions; + + +import machinecoding.uditagarwal.chess.model.Piece; + +/** + * This condition allows a move only if cell is making a move from its initial position. That is first move ever. + */ +public class MoveBaseConditionFirstMove implements MoveBaseCondition { + + public boolean isBaseConditionFullfilled(Piece piece) { + return piece.getNumMoves() == 0; + } +} diff --git a/src/main/java/machinecoding/uditagarwal/chess/conditions/NoMoveBaseCondition.java b/src/main/java/machinecoding/uditagarwal/chess/conditions/NoMoveBaseCondition.java new file mode 100644 index 0000000..2c06b64 --- /dev/null +++ b/src/main/java/machinecoding/uditagarwal/chess/conditions/NoMoveBaseCondition.java @@ -0,0 +1,13 @@ +package machinecoding.uditagarwal.chess.conditions; + + +import machinecoding.uditagarwal.chess.model.Piece; + +/** + * Helper implementation to use when there is no associated base condition with a move. + */ +public class NoMoveBaseCondition implements MoveBaseCondition { + public boolean isBaseConditionFullfilled(Piece piece) { + return true; + } +} diff --git a/src/main/java/machinecoding/uditagarwal/chess/conditions/PieceCellOccupyBlocker.java b/src/main/java/machinecoding/uditagarwal/chess/conditions/PieceCellOccupyBlocker.java new file mode 100644 index 0000000..605759e --- /dev/null +++ b/src/main/java/machinecoding/uditagarwal/chess/conditions/PieceCellOccupyBlocker.java @@ -0,0 +1,15 @@ +package machinecoding.uditagarwal.chess.conditions; + + +import machinecoding.uditagarwal.chess.model.Board; +import machinecoding.uditagarwal.chess.model.Cell; +import machinecoding.uditagarwal.chess.model.Piece; +import machinecoding.uditagarwal.chess.model.Player; + +/** + * This check tells whether a piece can occupy a given cell in the board or not. + */ +public interface PieceCellOccupyBlocker { + + boolean isCellNonOccupiableForPiece(Cell cell, Piece piece, Board board, Player player); +} diff --git a/src/main/java/machinecoding/uditagarwal/chess/conditions/PieceCellOccupyBlockerFactory.java b/src/main/java/machinecoding/uditagarwal/chess/conditions/PieceCellOccupyBlockerFactory.java new file mode 100644 index 0000000..2a0f3be --- /dev/null +++ b/src/main/java/machinecoding/uditagarwal/chess/conditions/PieceCellOccupyBlockerFactory.java @@ -0,0 +1,22 @@ +package machinecoding.uditagarwal.chess.conditions; + + +import java.util.List; + +/** + * Factory class to give cell occupy blockers for different scenarios. + */ +public class PieceCellOccupyBlockerFactory { + + public static PieceCellOccupyBlocker defaultBaseBlocker() { + return new PieceCellOccupyBlockerSelfPiece(); + } + + public static List defaultAdditionalBlockers() { + return List.of(new PieceCellOccupyBlockerKingCheck()); + } + + public static List kingCheckEvaluationBlockers() { + return List.of(); + } +} diff --git a/src/main/java/machinecoding/uditagarwal/chess/conditions/PieceCellOccupyBlockerKingCheck.java b/src/main/java/machinecoding/uditagarwal/chess/conditions/PieceCellOccupyBlockerKingCheck.java new file mode 100644 index 0000000..5d5a630 --- /dev/null +++ b/src/main/java/machinecoding/uditagarwal/chess/conditions/PieceCellOccupyBlockerKingCheck.java @@ -0,0 +1,22 @@ +package machinecoding.uditagarwal.chess.conditions; + + +import machinecoding.uditagarwal.chess.model.Board; +import machinecoding.uditagarwal.chess.model.Cell; +import machinecoding.uditagarwal.chess.model.Piece; +import machinecoding.uditagarwal.chess.model.Player; + +/** + * This tells whether making piece move to a cell will attract check for king. + */ +public class PieceCellOccupyBlockerKingCheck implements PieceCellOccupyBlocker { + + @Override + public boolean isCellNonOccupiableForPiece(final Cell cell, final Piece piece, final Board board, Player player) { + Cell pieceOriginalCell = piece.getCurrentCell(); + piece.setCurrentCell(cell); + boolean playerGettingCheckByMove = board.isPlayerOnCheck(player); + piece.setCurrentCell(pieceOriginalCell); + return playerGettingCheckByMove; + } +} diff --git a/src/main/java/machinecoding/uditagarwal/chess/conditions/PieceCellOccupyBlockerSelfPiece.java b/src/main/java/machinecoding/uditagarwal/chess/conditions/PieceCellOccupyBlockerSelfPiece.java new file mode 100644 index 0000000..6b78167 --- /dev/null +++ b/src/main/java/machinecoding/uditagarwal/chess/conditions/PieceCellOccupyBlockerSelfPiece.java @@ -0,0 +1,21 @@ +package machinecoding.uditagarwal.chess.conditions; + + +import machinecoding.uditagarwal.chess.model.Board; +import machinecoding.uditagarwal.chess.model.Cell; +import machinecoding.uditagarwal.chess.model.Piece; +import machinecoding.uditagarwal.chess.model.Player; + +/** + * This tells that a cell cannot occupy a cell if that cell already has any piece from the same player. + */ +public class PieceCellOccupyBlockerSelfPiece implements PieceCellOccupyBlocker { + + @Override + public boolean isCellNonOccupiableForPiece(Cell cell, Piece piece, Board board, Player player) { + if (cell.isFree()) { + return false; + } + return cell.getCurrentPiece().getColor() == piece.getColor(); + } +} diff --git a/src/main/java/machinecoding/uditagarwal/chess/conditions/PieceMoveFurtherCondition.java b/src/main/java/machinecoding/uditagarwal/chess/conditions/PieceMoveFurtherCondition.java new file mode 100644 index 0000000..6a53e70 --- /dev/null +++ b/src/main/java/machinecoding/uditagarwal/chess/conditions/PieceMoveFurtherCondition.java @@ -0,0 +1,14 @@ +package machinecoding.uditagarwal.chess.conditions; + + +import machinecoding.uditagarwal.chess.model.Board; +import machinecoding.uditagarwal.chess.model.Cell; +import machinecoding.uditagarwal.chess.model.Piece; + +/** + * Condition interface to tell whether a piece can further move from a cell. + */ +public interface PieceMoveFurtherCondition { + + boolean canPieceMoveFurtherFromCell(Piece piece, Cell cell, Board board); +} diff --git a/src/main/java/machinecoding/uditagarwal/chess/conditions/PieceMoveFurtherConditionDefault.java b/src/main/java/machinecoding/uditagarwal/chess/conditions/PieceMoveFurtherConditionDefault.java new file mode 100644 index 0000000..1d4686e --- /dev/null +++ b/src/main/java/machinecoding/uditagarwal/chess/conditions/PieceMoveFurtherConditionDefault.java @@ -0,0 +1,18 @@ +package machinecoding.uditagarwal.chess.conditions; + + +import machinecoding.uditagarwal.chess.model.Board; +import machinecoding.uditagarwal.chess.model.Cell; +import machinecoding.uditagarwal.chess.model.Piece; + +/** + * Default condition for moving further. By default, a piece is allowed to move from a cell only if the cell was free + * when it came there. + */ +public class PieceMoveFurtherConditionDefault implements PieceMoveFurtherCondition { + + @Override + public boolean canPieceMoveFurtherFromCell(Piece piece, Cell cell, Board board) { + return cell.isFree(); + } +} diff --git a/src/main/java/machinecoding/uditagarwal/chess/exceptions/InvalidMoveException.java b/src/main/java/machinecoding/uditagarwal/chess/exceptions/InvalidMoveException.java new file mode 100644 index 0000000..c23a170 --- /dev/null +++ b/src/main/java/machinecoding/uditagarwal/chess/exceptions/InvalidMoveException.java @@ -0,0 +1,7 @@ +package machinecoding.uditagarwal.chess.exceptions; + +/** + * Exception when move done by a player for a piece is invalid. + */ +public class InvalidMoveException extends RuntimeException { +} diff --git a/src/main/java/machinecoding/uditagarwal/chess/exceptions/PieceNotFoundException.java b/src/main/java/machinecoding/uditagarwal/chess/exceptions/PieceNotFoundException.java new file mode 100644 index 0000000..330a8a1 --- /dev/null +++ b/src/main/java/machinecoding/uditagarwal/chess/exceptions/PieceNotFoundException.java @@ -0,0 +1,7 @@ +package machinecoding.uditagarwal.chess.exceptions; + +/** + * Exception if piece to be fetched is not present. + */ +public class PieceNotFoundException extends RuntimeException { +} diff --git a/src/main/java/machinecoding/uditagarwal/chess/helpers/ListHelpers.java b/src/main/java/machinecoding/uditagarwal/chess/helpers/ListHelpers.java new file mode 100644 index 0000000..90d9fd7 --- /dev/null +++ b/src/main/java/machinecoding/uditagarwal/chess/helpers/ListHelpers.java @@ -0,0 +1,26 @@ +package machinecoding.uditagarwal.chess.helpers; + +import java.util.ArrayList; +import java.util.List; + +/** + * Helper class related to {@link List}. + */ +public class ListHelpers { + + /** + * Helper method to remove duplicate objects from a list. + * + * @param list List from which duplicated have to be removed. + * @return List without duplicates. + */ + public static List removeDuplicates(List list) { + List newList = new ArrayList(); + for (T element : list) { + if (!newList.contains(element)) { + newList.add(element); + } + } + return newList; + } +} diff --git a/src/main/java/machinecoding/uditagarwal/chess/model/Board.java b/src/main/java/machinecoding/uditagarwal/chess/model/Board.java new file mode 100644 index 0000000..3334bdc --- /dev/null +++ b/src/main/java/machinecoding/uditagarwal/chess/model/Board.java @@ -0,0 +1,95 @@ +package machinecoding.uditagarwal.chess.model; + + +import lombok.Getter; +import machinecoding.uditagarwal.chess.conditions.PieceCellOccupyBlocker; + +import java.util.List; + +import static machinecoding.uditagarwal.chess.conditions.PieceCellOccupyBlockerFactory.kingCheckEvaluationBlockers; + +/** + * Model object for a board of chess game. A board has a size and a grid of cells. + */ +@Getter +public class Board { + int boardSize; + Cell[][] cells; + + public Board(int boardSize, Cell[][] cells) { + this.boardSize = boardSize; + this.cells = cells; + } + + /** + * Helper method to return cell to the left of current cell. + */ + public Cell getLeftCell(Cell cell) { + return getCellAtLocation(cell.getX(), cell.getY() - 1); + } + + /** + * Helper method to return cell to the right of current cell. + */ + public Cell getRightCell(Cell cell) { + return getCellAtLocation(cell.getX(), cell.getY() + 1); + } + + /** + * Helper method to return cell to the up of current cell. + */ + public Cell getUpCell(Cell cell) { + return getCellAtLocation(cell.getX() + 1, cell.getY()); + } + + /** + * Helper method to return cell to the down of current cell. + */ + public Cell getDownCell(Cell cell) { + return getCellAtLocation(cell.getX() - 1, cell.getY()); + } + + /** + * Helper method to return cell at a given location. + */ + public Cell getCellAtLocation(int x, int y) { + if (x >= boardSize || x < 0) { + return null; + } + if (y >= boardSize || y < 0) { + return null; + } + + return cells[x][y]; + } + + /** + * Helper method to determine whether the player is on check on the current board. + */ + public boolean isPlayerOnCheck(Player player) { + return checkIfPieceCanBeKilled(player.getPiece(PieceType.KING), kingCheckEvaluationBlockers(), player); + } + + /** + * Method to check if the piece can be killed currently by the opponent as per the current board configuration. + * + * @param targetPiece Piece which is to be checked. + * @param cellOccupyBlockers Blockers which make cell non occupiable. + * @param player Player whose piece has to be checked. + * @return Boolean indicating whether the piece is in danger or not. + */ + public boolean checkIfPieceCanBeKilled(Piece targetPiece, List cellOccupyBlockers, Player player) { + for (int i = 0; i < getBoardSize(); i++) { + for (int j = 0; j < getBoardSize(); j++) { + Piece currentPiece = getCellAtLocation(i, j).getCurrentPiece(); + if (currentPiece != null && !currentPiece.isPieceFromSamePlayer(targetPiece)) { + List nextPossibleCells = currentPiece.nextPossibleCells(this, cellOccupyBlockers, player); + if (nextPossibleCells.contains(targetPiece.getCurrentCell())) { + return true; + } + } + } + } + return false; + } +} diff --git a/src/main/java/machinecoding/uditagarwal/chess/model/Cell.java b/src/main/java/machinecoding/uditagarwal/chess/model/Cell.java new file mode 100644 index 0000000..09435f1 --- /dev/null +++ b/src/main/java/machinecoding/uditagarwal/chess/model/Cell.java @@ -0,0 +1,28 @@ +package machinecoding.uditagarwal.chess.model; + + +import lombok.Getter; +import lombok.Setter; + +/** + * Model class representing the single cell of a board. A cell has a location in the grid which is represented by x + * and y location. + */ +@Getter +public class Cell { + + private int x; + private int y; + + public Cell(int x, int y) { + this.x = x; + this.y = y; + } + + @Setter + private Piece currentPiece; + + public boolean isFree() { + return currentPiece == null; + } +} diff --git a/src/main/java/machinecoding/uditagarwal/chess/model/Color.java b/src/main/java/machinecoding/uditagarwal/chess/model/Color.java new file mode 100644 index 0000000..5d8baf6 --- /dev/null +++ b/src/main/java/machinecoding/uditagarwal/chess/model/Color.java @@ -0,0 +1,6 @@ +package machinecoding.uditagarwal.chess.model; + +public enum Color { + BLACK, + WHITE +} \ No newline at end of file diff --git a/src/main/java/machinecoding/uditagarwal/chess/model/Piece.java b/src/main/java/machinecoding/uditagarwal/chess/model/Piece.java new file mode 100644 index 0000000..0bbbe96 --- /dev/null +++ b/src/main/java/machinecoding/uditagarwal/chess/model/Piece.java @@ -0,0 +1,94 @@ +package machinecoding.uditagarwal.chess.model; + + +import lombok.Getter; +import lombok.NonNull; +import lombok.Setter; +import machinecoding.uditagarwal.chess.conditions.PieceCellOccupyBlocker; +import machinecoding.uditagarwal.chess.exceptions.InvalidMoveException; +import machinecoding.uditagarwal.chess.moves.PossibleMovesProvider; + +import java.util.ArrayList; +import java.util.List; + +import static machinecoding.uditagarwal.chess.helpers.ListHelpers.removeDuplicates; + +/** + * Model class representing a single piece which can be moved on the board. + */ +@Getter +public class Piece { + private boolean isKilled = false; + private final Color color; + private final List movesProviders; + private Integer numMoves = 0; + PieceType pieceType; + + @Setter + @NonNull + private Cell currentCell; + + public Piece(@NonNull final Color color, @NonNull final List movesProviders, @NonNull final PieceType pieceType) { + this.color = color; + this.movesProviders = movesProviders; + this.pieceType = pieceType; + } + + public void killIt() { + this.isKilled = true; + } + + /** + * Method to move piece from current cell to a given cell. + */ + public void move(Player player, Cell toCell, Board board, List additionalBlockers) { + if (isKilled) { + throw new InvalidMoveException(); + } + List nextPossibleCells = nextPossibleCells(board, additionalBlockers, player); + if (!nextPossibleCells.contains(toCell)) { + throw new InvalidMoveException(); + } + + killPieceInCell(toCell); + this.currentCell.setCurrentPiece(null); + this.currentCell = toCell; + this.currentCell.setCurrentPiece(this); + this.numMoves++; + } + + /** + * Helper method to kill a piece in a given cell. + */ + private void killPieceInCell(Cell targetCell) { + if (targetCell.getCurrentPiece() != null) { + targetCell.getCurrentPiece().killIt(); + } + } + + /** + * Method which tells what are all next possible cells to which the current piece can move from the current cell. + * + * @param board Board on which the piece is present. + * @param additionalBlockers Blockers which make a cell non-occupiable for a piece. + * @param player Player who owns the piece. + * @return List of all next possible cells. + */ + public List nextPossibleCells(Board board, List additionalBlockers, Player player) { + List result = new ArrayList<>(); + for (PossibleMovesProvider movesProvider : this.movesProviders) { + List cells = movesProvider.possibleMoves(this, board, additionalBlockers, player); + if (cells != null) { + result.addAll(cells); + } + } + return removeDuplicates(result); + } + + /** + * Helper method to check if two pieces belong to same player. + */ + public boolean isPieceFromSamePlayer(Piece piece) { + return piece.getColor().equals(this.color); + } +} diff --git a/src/main/java/machinecoding/uditagarwal/chess/model/PieceType.java b/src/main/java/machinecoding/uditagarwal/chess/model/PieceType.java new file mode 100644 index 0000000..8fac3fb --- /dev/null +++ b/src/main/java/machinecoding/uditagarwal/chess/model/PieceType.java @@ -0,0 +1,10 @@ +package machinecoding.uditagarwal.chess.model; + +public enum PieceType { + KING, + QUEEN, + ROOK, + KNIGHT, + BISHOP, + PAWN +} diff --git a/src/main/java/machinecoding/uditagarwal/chess/model/Player.java b/src/main/java/machinecoding/uditagarwal/chess/model/Player.java new file mode 100644 index 0000000..054cdcb --- /dev/null +++ b/src/main/java/machinecoding/uditagarwal/chess/model/Player.java @@ -0,0 +1,33 @@ +package machinecoding.uditagarwal.chess.model; + + +import lombok.Getter; +import machinecoding.uditagarwal.chess.exceptions.PieceNotFoundException; +import machinecoding.uditagarwal.gameplay.contracts.PlayerMove; + +import java.util.List; + +/** + * Abstract model class representing a player. This is abstract because we are not sure how player is going to make his + * move. If the player is a local player, then he can move locally. A player can also be a network based player. + * So, depending on the type of player, he can make move in its own way. + */ +@Getter +public abstract class Player { + List pieces; + + public Player(List pieces) { + this.pieces = pieces; + } + + public Piece getPiece(PieceType pieceType) { + for (Piece piece : getPieces()) { + if (piece.getPieceType() == pieceType) { + return piece; + } + } + throw new PieceNotFoundException(); + } + + abstract public PlayerMove makeMove(); +} diff --git a/src/main/java/machinecoding/uditagarwal/chess/moves/NextCellProvider.java b/src/main/java/machinecoding/uditagarwal/chess/moves/NextCellProvider.java new file mode 100644 index 0000000..db16d16 --- /dev/null +++ b/src/main/java/machinecoding/uditagarwal/chess/moves/NextCellProvider.java @@ -0,0 +1,11 @@ +package machinecoding.uditagarwal.chess.moves; + + +import machinecoding.uditagarwal.chess.model.Cell; + +/** + * Given a cell, it will provide next cell which we can reach to. + */ +interface NextCellProvider { + Cell nextCell(Cell cell); +} diff --git a/src/main/java/machinecoding/uditagarwal/chess/moves/PossibleMovesProvider.java b/src/main/java/machinecoding/uditagarwal/chess/moves/PossibleMovesProvider.java new file mode 100644 index 0000000..70dd6a2 --- /dev/null +++ b/src/main/java/machinecoding/uditagarwal/chess/moves/PossibleMovesProvider.java @@ -0,0 +1,85 @@ +package machinecoding.uditagarwal.chess.moves; + + + +import machinecoding.uditagarwal.chess.conditions.MoveBaseCondition; +import machinecoding.uditagarwal.chess.conditions.PieceCellOccupyBlocker; +import machinecoding.uditagarwal.chess.conditions.PieceMoveFurtherCondition; +import machinecoding.uditagarwal.chess.model.Board; +import machinecoding.uditagarwal.chess.model.Cell; +import machinecoding.uditagarwal.chess.model.Piece; +import machinecoding.uditagarwal.chess.model.Player; + +import java.util.ArrayList; +import java.util.List; + +/** + * Provider class which returns all the possible cells for a given type of moves. For example, horizontal type of move + * will give all the cells which can be reached by making only horizontal moves. + */ +public abstract class PossibleMovesProvider { + int maxSteps; + MoveBaseCondition baseCondition; + PieceMoveFurtherCondition moveFurtherCondition; + PieceCellOccupyBlocker baseBlocker; + + public PossibleMovesProvider(int maxSteps, MoveBaseCondition baseCondition, PieceMoveFurtherCondition moveFurtherCondition, PieceCellOccupyBlocker baseBlocker) { + this.maxSteps = maxSteps; + this.baseCondition = baseCondition; + this.moveFurtherCondition = moveFurtherCondition; + this.baseBlocker = baseBlocker; + } + + /** + * Public method which actually gives all possible cells which can be reached via current type of move. + */ + public List possibleMoves(Piece piece, Board inBoard, List additionalBlockers, Player player) { + if (baseCondition.isBaseConditionFullfilled(piece)) { + return possibleMovesAsPerCurrentType(piece, inBoard, additionalBlockers, player); + } + return null; + } + + /** + * Abstract method which needs to be implemented by each type of move to give possible moves as per their behaviour. + */ + protected abstract List possibleMovesAsPerCurrentType(Piece piece, Board board, List additionalBlockers, Player player); + + /** + * Helper method used by all the sub types to create the list of cells which can be reached. + */ + protected List findAllNextMoves(Piece piece, NextCellProvider nextCellProvider, Board board, List cellOccupyBlockers, Player player) { + List result = new ArrayList<>(); + Cell nextCell = nextCellProvider.nextCell(piece.getCurrentCell()); + int numSteps = 1; + while (nextCell != null && numSteps <= maxSteps) { + if (checkIfCellCanBeOccupied(piece, nextCell, board, cellOccupyBlockers, player)) { + result.add(nextCell); + } + if (!moveFurtherCondition.canPieceMoveFurtherFromCell(piece, nextCell, board)) { + break; + } + + nextCell = nextCellProvider.nextCell(nextCell); + numSteps++; + } + return result; + } + + /** + * Helper method which checks if a given cell can be occupied by the piece or not. It makes use of list of + * {@link PieceCellOccupyBlocker}s passed to it while checking. Also each move has one base blocker which it should + * also check. + */ + private boolean checkIfCellCanBeOccupied(Piece piece, Cell cell, Board board, List additionalBlockers, Player player) { + if (baseBlocker != null && baseBlocker.isCellNonOccupiableForPiece(cell, piece, board, player)) { + return false; + } + for (PieceCellOccupyBlocker cellOccupyBlocker : additionalBlockers) { + if (cellOccupyBlocker.isCellNonOccupiableForPiece(cell, piece, board, player)) { + return false; + } + } + return true; + } +} diff --git a/src/main/java/machinecoding/uditagarwal/chess/moves/PossibleMovesProviderDiagonal.java b/src/main/java/machinecoding/uditagarwal/chess/moves/PossibleMovesProviderDiagonal.java new file mode 100644 index 0000000..78ab858 --- /dev/null +++ b/src/main/java/machinecoding/uditagarwal/chess/moves/PossibleMovesProviderDiagonal.java @@ -0,0 +1,26 @@ +package machinecoding.uditagarwal.chess.moves; + + +import machinecoding.uditagarwal.chess.conditions.MoveBaseCondition; +import machinecoding.uditagarwal.chess.conditions.PieceCellOccupyBlocker; +import machinecoding.uditagarwal.chess.conditions.PieceMoveFurtherCondition; +import machinecoding.uditagarwal.chess.model.Board; +import machinecoding.uditagarwal.chess.model.Cell; +import machinecoding.uditagarwal.chess.model.Piece; +import machinecoding.uditagarwal.chess.model.Player; + +import java.util.List; + +public class PossibleMovesProviderDiagonal extends PossibleMovesProvider { + + + public PossibleMovesProviderDiagonal(int maxSteps, MoveBaseCondition baseCondition, + PieceMoveFurtherCondition moveFurtherCondition, PieceCellOccupyBlocker baseBlocker) { + super(maxSteps, baseCondition, moveFurtherCondition, baseBlocker); + } + + @Override + protected List possibleMovesAsPerCurrentType(Piece piece, Board board, List additionalBlockers, Player player) { + return null; + } +} diff --git a/src/main/java/machinecoding/uditagarwal/chess/moves/PossibleMovesProviderHorizontal.java b/src/main/java/machinecoding/uditagarwal/chess/moves/PossibleMovesProviderHorizontal.java new file mode 100644 index 0000000..b2ac8b9 --- /dev/null +++ b/src/main/java/machinecoding/uditagarwal/chess/moves/PossibleMovesProviderHorizontal.java @@ -0,0 +1,28 @@ +package machinecoding.uditagarwal.chess.moves; + + +import machinecoding.uditagarwal.chess.conditions.MoveBaseCondition; +import machinecoding.uditagarwal.chess.conditions.PieceCellOccupyBlocker; +import machinecoding.uditagarwal.chess.conditions.PieceMoveFurtherCondition; +import machinecoding.uditagarwal.chess.model.Board; +import machinecoding.uditagarwal.chess.model.Cell; +import machinecoding.uditagarwal.chess.model.Piece; +import machinecoding.uditagarwal.chess.model.Player; + +import java.util.ArrayList; +import java.util.List; + +public class PossibleMovesProviderHorizontal extends PossibleMovesProvider { + + public PossibleMovesProviderHorizontal(int maxSteps, MoveBaseCondition baseCondition, + PieceMoveFurtherCondition moveFurtherCondition, PieceCellOccupyBlocker baseBlocker) { + super(maxSteps, baseCondition, moveFurtherCondition, baseBlocker); + } + + protected List possibleMovesAsPerCurrentType(Piece piece, final Board board, List additionalBlockers, Player player) { + List result = new ArrayList<>(); + result.addAll(findAllNextMoves(piece, board::getLeftCell, board, additionalBlockers, player)); + result.addAll(findAllNextMoves(piece, board::getRightCell, board, additionalBlockers, player)); + return result; + } +} diff --git a/src/main/java/machinecoding/uditagarwal/chess/moves/PossibleMovesProviderVertical.java b/src/main/java/machinecoding/uditagarwal/chess/moves/PossibleMovesProviderVertical.java new file mode 100644 index 0000000..1a01eea --- /dev/null +++ b/src/main/java/machinecoding/uditagarwal/chess/moves/PossibleMovesProviderVertical.java @@ -0,0 +1,39 @@ +package machinecoding.uditagarwal.chess.moves; + + +import machinecoding.uditagarwal.chess.conditions.MoveBaseCondition; +import machinecoding.uditagarwal.chess.conditions.PieceCellOccupyBlocker; +import machinecoding.uditagarwal.chess.conditions.PieceMoveFurtherCondition; +import machinecoding.uditagarwal.chess.model.Board; +import machinecoding.uditagarwal.chess.model.Cell; +import machinecoding.uditagarwal.chess.model.Piece; +import machinecoding.uditagarwal.chess.model.Player; + +import java.util.ArrayList; +import java.util.List; + +import static machinecoding.uditagarwal.chess.moves.VerticalMoveDirection.*; + +public class PossibleMovesProviderVertical extends PossibleMovesProvider { + private VerticalMoveDirection verticalMoveDirection; + + public PossibleMovesProviderVertical(int maxSteps, MoveBaseCondition baseCondition, + PieceMoveFurtherCondition moveFurtherCondition, PieceCellOccupyBlocker baseBlocker, + VerticalMoveDirection verticalMoveDirection) { + super(maxSteps, baseCondition, moveFurtherCondition, baseBlocker); + this.verticalMoveDirection = verticalMoveDirection; + } + + + @Override + protected List possibleMovesAsPerCurrentType(Piece piece, Board board, List additionalBlockers, Player player) { + List result = new ArrayList<>(); + if (this.verticalMoveDirection == UP || this.verticalMoveDirection == BOTH) { + result.addAll(findAllNextMoves(piece, board::getUpCell, board, additionalBlockers, player)); + } + if (this.verticalMoveDirection == DOWN || this.verticalMoveDirection == BOTH) { + result.addAll(findAllNextMoves(piece, board::getDownCell, board, additionalBlockers, player)); + } + return result; + } +} diff --git a/src/main/java/machinecoding/uditagarwal/chess/moves/VerticalMoveDirection.java b/src/main/java/machinecoding/uditagarwal/chess/moves/VerticalMoveDirection.java new file mode 100644 index 0000000..4bf9ded --- /dev/null +++ b/src/main/java/machinecoding/uditagarwal/chess/moves/VerticalMoveDirection.java @@ -0,0 +1,7 @@ +package machinecoding.uditagarwal.chess.moves; + +public enum VerticalMoveDirection { + UP, + DOWN, + BOTH +} diff --git a/src/main/java/machinecoding/uditagarwal/gameplay/GameController.java b/src/main/java/machinecoding/uditagarwal/gameplay/GameController.java new file mode 100644 index 0000000..3d3bfde --- /dev/null +++ b/src/main/java/machinecoding/uditagarwal/gameplay/GameController.java @@ -0,0 +1,24 @@ +package machinecoding.uditagarwal.gameplay; + + +import machinecoding.uditagarwal.chess.model.Board; +import machinecoding.uditagarwal.chess.model.Player; +import machinecoding.uditagarwal.gameplay.contracts.PlayerMove; + +import java.util.List; + +import static machinecoding.uditagarwal.chess.conditions.PieceCellOccupyBlockerFactory.defaultAdditionalBlockers; + +public class GameController { + + public static void gameplay(List players, Board board) { + int currentPlayer = 0; + while (true) { + Player player = players.get(currentPlayer); + //TODO: Check if current player has any move possible. If no move possible, then its checkmate. + PlayerMove playerMove = player.makeMove(); + playerMove.getPiece().move(player, playerMove.getToCell(), board, defaultAdditionalBlockers()); + currentPlayer = (currentPlayer + 1) % players.size(); + } + } +} diff --git a/src/main/java/machinecoding/uditagarwal/gameplay/contracts/PlayerMove.java b/src/main/java/machinecoding/uditagarwal/gameplay/contracts/PlayerMove.java new file mode 100644 index 0000000..f56ed3f --- /dev/null +++ b/src/main/java/machinecoding/uditagarwal/gameplay/contracts/PlayerMove.java @@ -0,0 +1,13 @@ +package machinecoding.uditagarwal.gameplay.contracts; + + +import lombok.Getter; +import machinecoding.uditagarwal.chess.model.Cell; +import machinecoding.uditagarwal.chess.model.Piece; + +@Getter +public class PlayerMove { + + Piece piece; + Cell toCell; +} diff --git a/src/main/java/microsoftassesment/AutocompleteSystem.java b/src/main/java/microsoftassesment/AutocompleteSystem.java new file mode 100644 index 0000000..3036215 --- /dev/null +++ b/src/main/java/microsoftassesment/AutocompleteSystem.java @@ -0,0 +1,33 @@ +package microsoftassesment; + +import java.util.*; +import java.util.stream.Collectors; + +class AutocompleteSystem { + private final Map cache = new HashMap<>(); + private String input = ""; + + public AutocompleteSystem(String[] sentences, int[] times) { + for (int i = 0; i < sentences.length; i++) { + cache.put(sentences[i], times[i]); + } + } + + public List input(char c) { + if (c == '#') { + Integer count = cache.getOrDefault(input, 0); + cache.put(input, ++count); + input = ""; + return Collections.emptyList(); + } + + input += c; + return cache.entrySet().stream() + .filter(e -> e.getKey().startsWith(input)) + .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()) + .thenComparing(Map.Entry.comparingByKey())) + .limit(3) + .map(Map.Entry::getKey) + .collect(Collectors.toCollection(ArrayList::new)); + } +} diff --git a/src/main/java/microsoftassesment/CropWords.java b/src/main/java/microsoftassesment/CropWords.java new file mode 100644 index 0000000..31c8cac --- /dev/null +++ b/src/main/java/microsoftassesment/CropWords.java @@ -0,0 +1,55 @@ +package microsoftassesment; + +public class CropWords { + + public static String cropWords(String message, int K){ + if (message == null || message.length() == 0){ + return ""; + } + // k greater than string, we return as it is; + if (message.length() <= K){ + return trim(message); + } + // we split at some point and move back wards till we meet a space, so that we can confirm that result has whole words only, then trim removes the space + StringBuilder sb = new StringBuilder(message.substring(0, K+1)); + while(sb.length() > 0 && sb.charAt(sb.length() - 1) != ' '){ + sb.deleteCharAt(sb.length() - 1); + } + + return trim(sb.toString()); + } + + private static String trim(String message){ + int i = message.length() - 1; + if (message.charAt(i) == ' '){ + while(i >= 0 && message.charAt(i) == ' '){ + i--; + } + if (i < 0){ + return ""; + } + return message.substring(0, i+1); + } + + + return message; + } + + public static void main(String[] args){ + System.out.println(cropWords("codility We test coders", 14).equals("codility We"));// === "codility We" + System.out.println(cropWords(" co de my", 5).equals(" co"));// === " co" + System.out.println(cropWords(" co de my", 7).equals(" co de"));// === " co de" + System.out.println(cropWords(" ", 2).equals(""));// === "" + System.out.println(cropWords(" re", 2).equals(""));// === "") //3 spaces before + System.out.println(cropWords(" c ", 3).equals(" c"));// === " c" + System.out.println(cropWords(" c d ", 5).equals(" c d"));// === " c d" + System.out.println(cropWords("co de my", 5).equals("co de"));// === "co de" + System.out.println(cropWords("Word", 4).equals("Word"));// === "Word" + System.out.println(cropWords("codility We test coders", 23).equals("codility We test coders"));// === "codility We test coders" + System.out.println(cropWords("withOutSpaces", 14).equals("withOutSpaces"));// === "withOutSpaces" + System.out.println(cropWords("", 14).equals(""));// === "" + System.out.println(cropWords("Separatedby hyphens", 14).equals("Separatedby"));// === "Separatedby" + System.out.println(cropWords(" Codility We test coders ", 14).equals(" Codility"));// === " Codility") //6 leading spaces + System.out.println(cropWords(" Codility We test coders ", 10).equals(""));// === "") //6 leading spaces + } +} diff --git a/src/main/java/microsoftassesment/LexicographicallySmallest.java b/src/main/java/microsoftassesment/LexicographicallySmallest.java new file mode 100644 index 0000000..8ad31df --- /dev/null +++ b/src/main/java/microsoftassesment/LexicographicallySmallest.java @@ -0,0 +1,52 @@ +package microsoftassesment; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.LinkedList; + +//Lexicographically the smallest string formed by removing at most one character. +// +// Example 1: +// +// Input: "abczd" +// Output: "abcd" + +public class LexicographicallySmallest { + + public String lexSmaller(String s){ + if(s==null || s.length()==0) return ""; + + Deque queue= new ArrayDeque<>(); + int k=1; + for(char c: s.toCharArray()){ + // k=1; + while (!queue.isEmpty() && queue.getLast()>=c && k>0){ + queue.removeLast(); + k--; + } + queue.addLast(c); + } + + StringBuilder sb= new StringBuilder(); + while (!queue.isEmpty()){ + sb.append(queue.removeFirst()); + } + + return sb.toString(); + } + + public String lexBuilder(String s ){ + StringBuilder str = new StringBuilder(s); + int i=0; + for(i=0; istr.charAt(i+1)){ + break; + } + } + + return str.deleteCharAt(i).toString(); + } + public static void main(String[] args) { + System.out.println(new LexicographicallySmallest().lexSmaller("abcde")); + } +} diff --git a/src/main/java/microsoftassesment/LongestSubStringWithout3ContiguousLetter.java b/src/main/java/microsoftassesment/LongestSubStringWithout3ContiguousLetter.java new file mode 100644 index 0000000..489f083 --- /dev/null +++ b/src/main/java/microsoftassesment/LongestSubStringWithout3ContiguousLetter.java @@ -0,0 +1,99 @@ +package microsoftassesment; + +import java.util.HashMap; +import java.util.Map; + + +//Given a string s containing only a and b, find longest substring of s such that s does not contain more than two contiguous occurrences of a and b. +// +// Example 1: +// +// Input: "aabbaaaaabb" +// Output: "aabbaa" +public class LongestSubStringWithout3ContiguousLetter { + + public String validLongestSubstring(String s) { + if (s.length() < 3) + return s; + int cur = 0, // current starting pointer for current substring + end = 1; // the look-ahead pointer that's at the end of substring + char c = s.charAt(0); // current character/letter + int count = 1; // counter for same letter consecutive appearance + int maxLen = 1; // result + int start = 0; // anchor pointer for the result's starting index + + while (end < s.length()) { + if (s.charAt(end) == c) { // saw the same letter again + count ++; + + // valid enough to consider to be a part of the substring + if (count == 2) { + if (end - cur + 1 > maxLen) { // length of the current substring is greater than maxlen + maxLen = end - cur + 1; // the "+1" is to include the full substring length + start = cur; // override to be the current maxlen's start index + } + } + else { + cur = end - 1; // count > 2 whenever we see 3 consec element, move the curr , therefore we need to start a new substring; reset curr + } + } + else { + c = s.charAt(end); // diff char/letter found; reset current char + count = 1; // reset same letter consecutive appearance counter + if (end - cur + 1 > maxLen) { // length of the current substring is greater than maxlen + maxLen = end - cur + 1; + start = cur; + } + } + end ++; + } + return s.substring(start, start + maxLen); + } + + public String slidingWindowPattern(String s){ + if(s==null) return null; + Map map= new HashMap<>(); + int left=0; + int right=0; + int count=0; + int start=0; + int result=0; + int end=0; + while(left=3){ + count++; + } + + while(count>0 ){ + char rightchar=s.charAt(right); + map.put(rightchar,map.getOrDefault(right,1)-1); + if(map.get(temp)<=2) count--; + right++; + //count--; + } + + if(result3 .... 4,3,3,2,1,1 -numdelete =1; +// i=2 +// freq[2]==freq1 so reduce freq[2]->2...4,3,2,2,1,1 numdelete=2; +// +// i=3 +// proceeding in a similar fashion the array would be 4.3.2,1,1,1 numdelete=3; +// +// i=4 +// 4,3,2,1,0,1 numdelete=4 +// +// i=5 +// we cannot have a frequency lesser than 0 ... it means that we would have to effectively delete that character making its frequency 0 +// 4,3,2,1,0,0 numdelete =5 +// +// and that's the solution + + + public int minDeletions(String str) { + Integer[] freqOfLetter; + // String str="qqqsaaaaadddddfafaafafeqqq"; + freqOfLetter = new Integer[26]; + + Arrays.fill(freqOfLetter,0); + + char[] s=str.toCharArray(); + for (int i = 0; i < s.length; i++) + { + freqOfLetter[s[i] - 'a']++; + } + + + int numDelete=0; + + Arrays.sort(freqOfLetter, Collections.reverseOrder()); + + for(int i=1;i=freqOfLetter[i-1]) { + if(freqOfLetter[i-1]==0) { + numDelete += freqOfLetter[i]; + freqOfLetter[i]=0; + } + else { + numDelete += freqOfLetter[i] - freqOfLetter[i - 1] + 1; + + freqOfLetter[i] = freqOfLetter[i - 1] - 1; + } + } + } + return numDelete; + } + + public static void main(String[] args) { + MinDeletionToMakeUniqueCount m = new MinDeletionToMakeUniqueCount(); + String[] testCases = new String[] {"qqqsaaaaadddddfafaafafeqqq", "aaaabbbb", "aabbbbcccdddd", "aaaaaabbbbbccccddddeeeeee", "abcdefghijkl", "aaaaaa", "aabbffddeaee", "llll", "example"}; + for (String test : testCases) System.out.println(test + ": " + m.minDeletions(test)); + } +} diff --git a/src/main/java/microsoftassesment/MinStepsToMakePileSameHeight.java b/src/main/java/microsoftassesment/MinStepsToMakePileSameHeight.java new file mode 100644 index 0000000..19241c5 --- /dev/null +++ b/src/main/java/microsoftassesment/MinStepsToMakePileSameHeight.java @@ -0,0 +1,47 @@ +package microsoftassesment; + +import java.util.Arrays; + +/** + * Alexa is given n piles of equal or unequal heights. In one step, Alexa can remove any number of boxes from the pile which has the maximum height and try to make it equal to the one which is just lower than the maximum height of the stack. + * Determine the minimum number of steps required to make all of the piles equal in height. + * + * Input: piles = [5, 2, 1] + * Output: 3 + * Explanation: + * Step 1: reducing 5 -> 2 [2, 2, 1] + * Step 2: reducing 2 -> 1 [2, 1, 1] + * Step 3: reducing 2 -> 1 [1, 1, 1] + * So final number of steps required is 3. + */ +public class MinStepsToMakePileSameHeight { + + public int minSteps(int[]arr){ + int result=0; + int distinctNumbers=0; + Arrays.sort(arr); + //1,2,5 + for(int i=1;i 1 != 2, i=2 => 2!=5 + distinctNumbers++; // else increase the distance count + } + + result+=distinctNumbers; // even if elements are same, we need to add it to result + } + return result; + } + + public static void main(String[] args) { + int[][] tests= new int[][]{ + {1, 2, 2, 2, 3, 4, 5, 6, 4, 12, 4}, + {1, 2, 3}, + {1, 2, 5}, + {1, 2, 3, 4, 5}}; + + for(int[] test: tests){ + System.out.println(new MinStepsToMakePileSameHeight().minSteps(test)); + } + } + + +} diff --git a/src/main/java/microsoftassesment/MinSwapsToGroupRedBalls.java b/src/main/java/microsoftassesment/MinSwapsToGroupRedBalls.java new file mode 100644 index 0000000..f2a7a7c --- /dev/null +++ b/src/main/java/microsoftassesment/MinSwapsToGroupRedBalls.java @@ -0,0 +1,36 @@ +package microsoftassesment; + +import java.util.ArrayList; +import java.util.List; + +public class MinSwapsToGroupRedBalls { + + public int solution(String s) { + List redIndices = getRedIndices(s); + int mid = redIndices.size() / 2; + int minSwaps = 0; + for (int i = 0; i < redIndices.size(); i++) { + // number of swaps for each R is the distance to mid, minus the number of R's between them + minSwaps += Math.abs(redIndices.get(mid) - redIndices.get(i)) - Math.abs(mid - i); + + } + return minSwaps; + } + + private static List getRedIndices(String s) { + List indices = new ArrayList<>(s.length()); + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) == 'R') { + indices.add(i); + } + } + return indices; + } + + public static void main(String[] args) { + String[] inputs= new String[] { "RRRWRR", "WRRWWR", "WWRWWWRWR", "WWW"}; + for(String input: inputs){ + System.out.println(input+" - "+new MinSwapsToGroupRedBalls().solution(input)); + } + } +} diff --git a/src/main/java/microsoftassesment/NumsWithEqualDigitSum.java b/src/main/java/microsoftassesment/NumsWithEqualDigitSum.java new file mode 100644 index 0000000..a2ff812 --- /dev/null +++ b/src/main/java/microsoftassesment/NumsWithEqualDigitSum.java @@ -0,0 +1,87 @@ +package microsoftassesment; + +import java.util.*; + +class Pair{ + String value; + int digitSum; + + public Pair(String value, int digitSum) { + this.value = value; + this.digitSum = digitSum; + } +} + +public class NumsWithEqualDigitSum { + + // given an array of integers, return max sum of 2 values whose digits adds up to same value + // [51,71,17,42] => 93 (51+42)=>(5+1==4+2) + // [42,33,60] => 102 (42+60)=> (4+2== 6+0) + + public int findMaxSumWithEqualDigits(int[] nums){ + if(nums==null || nums.length==0) return 0; + List inputs= new ArrayList<>(); + for (int i = 0; i < nums.length; i++) { + int sum=0; + int val=nums[i]; + while(val>0){ + sum+=val%10; + val/=10; + } + inputs.add(new Pair(String.valueOf(nums[i]),sum)); + } + PriorityQueue queue= new PriorityQueue<>((a,b)->Integer.compare(a.digitSum,b.digitSum)); + + int result=0; + queue.addAll(inputs); + while (!queue.isEmpty()){ + Pair first=queue.poll(); + if(queue.isEmpty()) break; + Pair second= queue.peek(); + if(first.digitSum==second.digitSum){ + second=queue.poll(); + result= Math.max(result, Integer.parseInt(first.value)+Integer.parseInt(second.value)); + queue.offer(new Pair(String.valueOf(Math.max(Integer.parseInt(first.value),Integer.parseInt(second.value))),first.digitSum)); + } + } + return result; + } + + public static void main(String[] args) { + System.out.println(new NumsWithEqualDigitSum().findMaxSumWithEqualDigits(new int[]{51,71,17,42})); + System.out.println(new NumsWithEqualDigitSum().findMaxSumWithEqualDigits(new int[]{42,33,60})); + System.out.println(new NumsWithEqualDigitSum().findMaxSumWithEqualDigits( new int[] {2053, 280, 780, 505, 690, 730, 4730, 951, 8331, 5079, 7252, 3675, 8969, 6904, 1194})); + System.out.println(new NumsWithEqualDigitSum().findMaxSumWithEqualDigits( new int[] {2053, 280, 780, 505, 690, 730, 4730, 951, 8331, 5079, 7252, 3675, 8969, 6904, 1194})); + //[2053, 280, 780, 505, 690, 730, 4730, 951, 8331, 5079, 7252, 3675, 8969, 6904, 1194] + + } + + + private int computeDigitSum(int a){ + // supposed to be valid for negative numbers and the output must be non-negative integer. + a = Math.abs(a); + int res = 0; + while(a > 0){ + res += a % 10; + a /= 10; + } + return res; + } + public int maxSum(int[] A){ + int N = A.length; + if(N <= 1) return -1; + Map map = new HashMap<>(); + int res = -1; + for(int i = 0; i < N; ++i){ + int digitsum = computeDigitSum(A[i]); + if(!map.containsKey(digitsum)){ + map.put(digitsum, A[i]); + } + else{ + res = Math.max(res, map.get(digitsum) + A[i]); + map.put(digitsum, Math.max(A[i], map.get(digitsum))); + } + } + return res; + } +} diff --git a/src/main/java/microsoftassesment/RemoveCharsMoreThanKOccurrence.java b/src/main/java/microsoftassesment/RemoveCharsMoreThanKOccurrence.java new file mode 100644 index 0000000..635ec7b --- /dev/null +++ b/src/main/java/microsoftassesment/RemoveCharsMoreThanKOccurrence.java @@ -0,0 +1,55 @@ +package microsoftassesment; + + +// given eedaaad and k=3 return eedaad +public class RemoveCharsMoreThanKOccurrence { + + public static String removeKConsecutiveChars(String s, int k) { + if (s == null || s.length() == 0) return null; + StringBuilder sb = new StringBuilder(); + sb.append(s.charAt(0)); + int cnt = 1; + for (int r = 1; r < s.length(); r++) { + char c = s.charAt(r); + if (c == s.charAt(r - 1)) + cnt++; + else { + cnt = 1; + } + if (cnt < k) + sb.append(c); + } + return sb.toString(); + + + } + + public static void main(String[] args) { + String[] inputs = {"eedaaad", "xxxtxxxz", "uuuuxaaaaxuuu"}; + for (String i : inputs) { + System.out.println(removeKConsecutiveChars(i, 3)); + } + + System.out.println(removeDuplicates("aaabcd")); + } + + public static String removeDuplicates(String A) { + int stptr = -1; + char[] arr = A.toCharArray(); + for (int i = 0; i < A.length(); i++) { + if (stptr == -1 || arr[i] != arr[stptr]) { + stptr++; + arr[stptr] = arr[i]; + } else { + stptr--; + } + } + StringBuilder ans = new StringBuilder(); + for (int i = 0; i <= stptr; i++) { + ans.append(arr[i]); + } + return new String(ans); + } + + +} diff --git a/src/main/java/microsoftassesment/StringWithout3ConsequitiveLetter.java b/src/main/java/microsoftassesment/StringWithout3ConsequitiveLetter.java new file mode 100644 index 0000000..7856443 --- /dev/null +++ b/src/main/java/microsoftassesment/StringWithout3ConsequitiveLetter.java @@ -0,0 +1,34 @@ +package microsoftassesment; + +public class StringWithout3ConsequitiveLetter { + + // Minimum number of swaps to make string without any instance of 3 contiguous identical letters, swaps permitted a transforms to b and vice versa + public int solution(String s) { + + + // Corner Cases: s === null, s.length < 3 - return 0 + + // Observations: for consecutive letter lengths divisible by 3 exactly, replace middle + // if in between lengths from numbers divisible by 3, then replace the position in the divisible by 3 position + // e.g. 3 consecutive - replace middle, 4 cons. - replace 3rd, 5 cons. - replace 3rd + // 6 cons. - turns back in the 3 cons. case, therefore, replace middle + + // Time Complexity: O(n) + // Space Complexity: O(1) + + if (s == null || s.length() < 3) { + return 0; + } + int moves = 0; + for (int i = 0 ; i < s.length(); i++) { + int runLength = 1; + for (; i + 1 < s.length() && s.charAt(i) == s.charAt(i + 1); i++) { + runLength++; + } + moves += runLength / 3; + } + return moves; + } + + +} diff --git a/src/main/java/multithreading/com/safecabs/Constants.java b/src/main/java/multithreading/com/safecabs/Constants.java new file mode 100644 index 0000000..579e69a --- /dev/null +++ b/src/main/java/multithreading/com/safecabs/Constants.java @@ -0,0 +1,6 @@ +package multithreading.com.safecabs; + +public class Constants { + public static int CAB_CAPACITY = 4; + public static int HALF_CAB_CAPACITY = CAB_CAPACITY/2; +} diff --git a/src/main/java/multithreading/com/safecabs/Gender.java b/src/main/java/multithreading/com/safecabs/Gender.java new file mode 100644 index 0000000..5f40fe1 --- /dev/null +++ b/src/main/java/multithreading/com/safecabs/Gender.java @@ -0,0 +1,7 @@ +package multithreading.com.safecabs; + +public enum Gender { + MALE, + FEMALE; + /* OTHERS - not relavent for requirement*/ +} diff --git a/src/main/java/multithreading/com/safecabs/app/AssignCab.java b/src/main/java/multithreading/com/safecabs/app/AssignCab.java new file mode 100644 index 0000000..6b873bc --- /dev/null +++ b/src/main/java/multithreading/com/safecabs/app/AssignCab.java @@ -0,0 +1,31 @@ +package multithreading.com.safecabs.app; + + +import multithreading.com.safecabs.cab.Cab; + +import java.util.concurrent.ArrayBlockingQueue; +import java.util.concurrent.Callable; + +public class AssignCab implements Callable { + + private ArrayBlockingQueue cabQueue; + + public AssignCab(ArrayBlockingQueue cabQueue) { + this.cabQueue = cabQueue; + } + + public Integer call() { + while (cabQueue.isEmpty()) { + try { + //All the cab requests should wait until a new cab appears + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + Cab assignedCab = cabQueue.poll(); + System.out.println("Assigned cab " + assignedCab.getId()); + return assignedCab.getId(); + } + +} diff --git a/src/main/java/multithreading/com/safecabs/app/CabProvider.java b/src/main/java/multithreading/com/safecabs/app/CabProvider.java new file mode 100644 index 0000000..72887be --- /dev/null +++ b/src/main/java/multithreading/com/safecabs/app/CabProvider.java @@ -0,0 +1,43 @@ +package multithreading.com.safecabs.app; + + +import multithreading.com.safecabs.Constants; +import multithreading.com.safecabs.cab.Cab; +import multithreading.com.safecabs.exceptions.UnRegisteredPassengerException; +import multithreading.com.safecabs.passenger.Passenger; +import multithreading.com.safecabs.passenger.RegisteredPassenger; + +import java.util.concurrent.ArrayBlockingQueue; + +public class CabProvider { + + private RegisteredPassenger registeredPassenger; + + private ArrayBlockingQueue cabQueue = new ArrayBlockingQueue<>(10); + + CustomCyclicBarrier customCyclicBarrier = new CustomCyclicBarrier(Constants.CAB_CAPACITY, new AssignCab(cabQueue)); + + public CabProvider(RegisteredPassenger registeredPassenger){ + + this.registeredPassenger = registeredPassenger; + } + + public void requestCab(Passenger passenger) throws UnRegisteredPassengerException { + if(registeredPassenger.isRegistered(passenger)) { + provideCab(passenger); + }else{ + throw new UnRegisteredPassengerException("Passenger "+passenger.getId()+" is not registered"); + } + } + + public void addNewAvailableCab(Cab cab) { + //The Cab which is available first should move first, so adding to Queue + cabQueue.add(cab); + } + + public void provideCab(Passenger passenger) { + CabRequest cabRequest = new CabRequest(customCyclicBarrier, passenger); + cabRequest.start(); + } + +} diff --git a/src/main/java/multithreading/com/safecabs/app/CabRequest.java b/src/main/java/multithreading/com/safecabs/app/CabRequest.java new file mode 100644 index 0000000..9ec31ce --- /dev/null +++ b/src/main/java/multithreading/com/safecabs/app/CabRequest.java @@ -0,0 +1,31 @@ +package multithreading.com.safecabs.app; + + +import multithreading.com.safecabs.passenger.Passenger; + +public class CabRequest extends Thread { + + private Passenger passenger; + private CustomCyclicBarrier customCyclicBarrier; + + public CabRequest(CustomCyclicBarrier customCyclicBarrier, Passenger passenger) { + this.customCyclicBarrier = customCyclicBarrier; + this.passenger = passenger; + } + + @Override + public void run() { + try { + System.out.println("Passenger " + passenger.getId() + " requesting cab " + passenger.getGender()); + + customCyclicBarrier.await(passenger); + + System.out.println("Passenger " + passenger.getId() + " boarding Cab"); + } catch (InterruptedException e) { + e.printStackTrace(); + } catch (Exception e) { + e.printStackTrace(); + } + } + +} diff --git a/src/main/java/multithreading/com/safecabs/app/CustomCyclicBarrier.java b/src/main/java/multithreading/com/safecabs/app/CustomCyclicBarrier.java new file mode 100644 index 0000000..df2e9fd --- /dev/null +++ b/src/main/java/multithreading/com/safecabs/app/CustomCyclicBarrier.java @@ -0,0 +1,94 @@ +package multithreading.com.safecabs.app; + + +import multithreading.com.safecabs.Constants; +import multithreading.com.safecabs.Gender; +import multithreading.com.safecabs.passenger.Passenger; + +import java.util.concurrent.Callable; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.Semaphore; +import java.util.concurrent.locks.ReentrantLock; + +public class CustomCyclicBarrier { + + Callable requestToAssignCab; + int malePassengerRequestCount = 0; + int femalePassengerRequestCount = 0; + CyclicBarrier barrier; + Semaphore maleWaitSemaphore = new Semaphore(0); + Semaphore femaleWaitSemaphore = new Semaphore(0); + ReentrantLock lock = new ReentrantLock(); + + public CustomCyclicBarrier(int cabCapacity, Callable requestToAssignCab) { + this.requestToAssignCab = requestToAssignCab; // Allocate Cab Request once passengers are ready + barrier = new CyclicBarrier(cabCapacity); // wait till cab capacity full + } + + public void await(Passenger passenger) throws Exception { + if (passenger.getGender() == Gender.MALE) { + seatMale(); + } else { + seatFemale(); + } + } + + private void seatFemale() throws Exception { + + lock.lock(); + + boolean leaderThread = false; + femalePassengerRequestCount++; + + if (femalePassengerRequestCount == Constants.CAB_CAPACITY) { + System.out.println("All Female Condition Meet"); + femaleWaitSemaphore.release(Constants.CAB_CAPACITY - 1); + femalePassengerRequestCount -= Constants.CAB_CAPACITY; + leaderThread = true; + } else if (femalePassengerRequestCount == Constants.HALF_CAB_CAPACITY && malePassengerRequestCount >= Constants.HALF_CAB_CAPACITY) { + System.out.println(Constants.HALF_CAB_CAPACITY+" Female And "+Constants.HALF_CAB_CAPACITY+" Male Condition Meet"); + femaleWaitSemaphore.release(Constants.HALF_CAB_CAPACITY - 1); + maleWaitSemaphore.release(Constants.HALF_CAB_CAPACITY); + malePassengerRequestCount -= Constants.HALF_CAB_CAPACITY; + femalePassengerRequestCount -= Constants.HALF_CAB_CAPACITY; + leaderThread = true; + } else { + lock.unlock(); + femaleWaitSemaphore.acquire(); + } + if (leaderThread) { + requestToAssignCab.call(); + lock.unlock(); + } + barrier.await(); + } + + private void seatMale() throws Exception { + lock.lock(); + boolean leaderThread = false; + malePassengerRequestCount++; + + if (malePassengerRequestCount == Constants.CAB_CAPACITY) { + System.out.println("All Male Condition Meet"); + maleWaitSemaphore.release(Constants.CAB_CAPACITY - 1); + malePassengerRequestCount -= Constants.CAB_CAPACITY; + leaderThread = true; + } else if (malePassengerRequestCount == Constants.HALF_CAB_CAPACITY && femalePassengerRequestCount >= Constants.HALF_CAB_CAPACITY) { + System.out.println(Constants.HALF_CAB_CAPACITY+" Male and "+Constants.HALF_CAB_CAPACITY+" Female Condition Meet"); + maleWaitSemaphore.release(Constants.HALF_CAB_CAPACITY - 1); + femaleWaitSemaphore.release(Constants.HALF_CAB_CAPACITY); + malePassengerRequestCount -= Constants.HALF_CAB_CAPACITY; + femalePassengerRequestCount -= Constants.HALF_CAB_CAPACITY; + leaderThread = true; + } else { + lock.unlock(); + maleWaitSemaphore.acquire(); + } + if (leaderThread) { + requestToAssignCab.call(); + lock.unlock(); + } + barrier.await(); + + } +} diff --git a/src/main/java/multithreading/com/safecabs/cab/Cab.java b/src/main/java/multithreading/com/safecabs/cab/Cab.java new file mode 100644 index 0000000..7b229cc --- /dev/null +++ b/src/main/java/multithreading/com/safecabs/cab/Cab.java @@ -0,0 +1,50 @@ +package multithreading.com.safecabs.cab; + + +import multithreading.com.safecabs.Constants; + +import java.util.Date; + +public class Cab { + + int id; + + int CAPACITY = Constants.CAB_CAPACITY; + + Date availableFrom; + + /* + Not required for current requirement + + Vehicle cab; // Vehicle made, model, year + + Driver owner; + */ + + public Cab(int id) { + this.id = id; + availableFrom = new Date(); + } + + public Cab(int id, Date availableFrom) { + this.id = id; + this.availableFrom = availableFrom; + } + + public int getId() { + return id; + } + + public int getCAPACITY() { + return CAPACITY; + } + + public Date getAvailableFrom() { + return availableFrom; + } + + public void setAvailableFrom(Date availableFrom) { + this.availableFrom = availableFrom; + } + +} diff --git a/src/main/java/multithreading/com/safecabs/client/ClientTest.java b/src/main/java/multithreading/com/safecabs/client/ClientTest.java new file mode 100644 index 0000000..11ecaed --- /dev/null +++ b/src/main/java/multithreading/com/safecabs/client/ClientTest.java @@ -0,0 +1,39 @@ +package multithreading.com.safecabs.client; + + +import multithreading.com.safecabs.Gender; +import multithreading.com.safecabs.app.CabProvider; +import multithreading.com.safecabs.cab.Cab; +import multithreading.com.safecabs.exceptions.UnRegisteredPassengerException; +import multithreading.com.safecabs.passenger.Passenger; +import multithreading.com.safecabs.passenger.RegisteredPassenger; + +import java.util.Random; + +public class ClientTest { + public static void main(String args[]) throws Exception { + + RegisteredPassenger registeredPassenger = new RegisteredPassenger(); + CabProvider cabProvider = new CabProvider(registeredPassenger); + + int cabId = 1; + for (int passengerId = 0; passengerId < 21; passengerId++) { + int rand = new Random().nextInt(2); + + Passenger passenger = new Passenger(passengerId, rand == 0 ? Gender.MALE : Gender.FEMALE); + + if (passengerId != 5) + registeredPassenger.register(passenger); // Passenger 5 not registered for testing + try { + cabProvider.requestCab(passenger); + } catch (UnRegisteredPassengerException e) { + System.out.println(e.getMessage()); + } + if (passengerId % 4 == 0) + cabProvider.addNewAvailableCab(new Cab(cabId++)); + + Thread.sleep(1000); + } + } + +} diff --git a/src/main/java/multithreading/com/safecabs/exceptions/UnRegisteredPassengerException.java b/src/main/java/multithreading/com/safecabs/exceptions/UnRegisteredPassengerException.java new file mode 100644 index 0000000..f678ba1 --- /dev/null +++ b/src/main/java/multithreading/com/safecabs/exceptions/UnRegisteredPassengerException.java @@ -0,0 +1,8 @@ +package multithreading.com.safecabs.exceptions; + +public class UnRegisteredPassengerException extends Exception { + + public UnRegisteredPassengerException(String s){ + super(s); + } +} diff --git a/src/main/java/multithreading/com/safecabs/passenger/Passenger.java b/src/main/java/multithreading/com/safecabs/passenger/Passenger.java new file mode 100644 index 0000000..948deb3 --- /dev/null +++ b/src/main/java/multithreading/com/safecabs/passenger/Passenger.java @@ -0,0 +1,24 @@ +package multithreading.com.safecabs.passenger; + + +import multithreading.com.safecabs.Gender; + +public class Passenger { + + private int id; + private Gender gender; + + public Passenger(int id, Gender gender) { + this.id = id; + this.gender = gender; + } + + public int getId() { + return id; + } + + public Gender getGender() { + return gender; + } + +} diff --git a/src/main/java/multithreading/com/safecabs/passenger/RegisteredPassenger.java b/src/main/java/multithreading/com/safecabs/passenger/RegisteredPassenger.java new file mode 100644 index 0000000..90545db --- /dev/null +++ b/src/main/java/multithreading/com/safecabs/passenger/RegisteredPassenger.java @@ -0,0 +1,24 @@ +package multithreading.com.safecabs.passenger; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +public class RegisteredPassenger { + private ConcurrentMap registeredPassengers; + + public RegisteredPassenger() { + + registeredPassengers = new ConcurrentHashMap<>(); + } + + public void register(Passenger passenger) { + + registeredPassengers.put(passenger.getId(), passenger); + } + + public boolean isRegistered(Passenger passenger) { + + return registeredPassengers.containsKey(passenger.getId()); + } + +} diff --git a/src/main/java/multithreading/educative/AsyncToSyncConverter.java b/src/main/java/multithreading/educative/AsyncToSyncConverter.java new file mode 100644 index 0000000..00fdafc --- /dev/null +++ b/src/main/java/multithreading/educative/AsyncToSyncConverter.java @@ -0,0 +1,58 @@ +package multithreading.educative; + +public class AsyncToSyncConverter { + public static void main(String args[]) throws Exception { + SynchronousExecutor executor = new SynchronousExecutor(); + executor.asynchronousExecution(() -> System.out.println("I am done")); + + System.out.println("main thread exiting..."); + } +} + +interface Callback { + void done(); +} + +class SynchronousExecutor extends Executor { + + @Override + public void asynchronousExecution(Callback callback) throws Exception { + + Object signal = new Object(); + final boolean[] isDone = new boolean[1]; + + Callback cb = () -> { + callback.done(); + synchronized (signal) { + signal.notify(); + isDone[0] = true; + } + }; + + // Call the asynchronous executor + super.asynchronousExecution(cb); + + synchronized (signal) { + while (!isDone[0]) { + signal.wait(); + } + } + + } +} + +class Executor { + + public void asynchronousExecution(Callback callback) throws Exception { + + Thread t = new Thread(() -> { + // Do some useful work + try { + Thread.sleep(5000); + } catch (InterruptedException ie) { + } + callback.done(); + }); + t.start(); + } +} diff --git a/src/main/java/multithreading/educative/BarberShopProblem.java b/src/main/java/multithreading/educative/BarberShopProblem.java new file mode 100644 index 0000000..8b9c2e3 --- /dev/null +++ b/src/main/java/multithreading/educative/BarberShopProblem.java @@ -0,0 +1,121 @@ +package multithreading.educative; + +import java.util.HashSet; +import java.util.concurrent.Semaphore; +import java.util.concurrent.locks.ReentrantLock; + +public class BarberShopProblem { + + final int CHAIRS = 3; + Semaphore waitForCustomerToEnterShop = new Semaphore(0); + Semaphore waitForBarberToGetReady = new Semaphore(0); + Semaphore waitForCustomerToLeaveChair = new Semaphore(0); + Semaphore waitForBarberToCutHair = new Semaphore(0); + int waitingCustomers = 0; + ReentrantLock lock = new ReentrantLock(); + int hairCutsGiven = 0; + + void barber() throws InterruptedException { + + while (true) { + // wait till a customer enters a shop + waitForCustomerToEnterShop.acquire(); + // let the customer know barber is ready + waitForBarberToGetReady.release(); + + hairCutsGiven++; + // close the shop for the day + if (hairCutsGiven >= 100) return; + + System.out.println("Barber cutting hair..." + hairCutsGiven); + Thread.sleep(50); + + // let customer thread know, haircut is done + waitForBarberToCutHair.release(); + + // wait for customer to leave the barber chair + waitForCustomerToLeaveChair.acquire(); + } + } + + void customerWalksIn() throws InterruptedException { + + lock.lock(); + if (waitingCustomers == CHAIRS) { + System.out.println("Customer walks out, all waiting waitForCustomerToEnter occupied"); + lock.unlock(); + return; + } + waitingCustomers++; + lock.unlock(); + + // Let the barber know, there's atleast 1 customer + waitForCustomerToEnterShop.release(); + // Wait for the barber to come take you to the salon chair when its your turn + waitForBarberToGetReady.acquire(); + + lock.lock(); + waitingCustomers--; + lock.unlock(); + + // Wait for haircut to complete + waitForBarberToCutHair.acquire(); + // Leave the barber chair and let barber thread know chair is vacant + waitForCustomerToLeaveChair.release(); + } + + public static void runTest() throws InterruptedException { + + HashSet set = new HashSet<>(); + final BarberShopProblem barberShopProblem = new BarberShopProblem(); + + Thread barberThread = new Thread(() -> { + try { + barberShopProblem.barber(); + } catch (InterruptedException ie) { + + } + }); + barberThread.start(); + + for (int i = 0; i < 10; i++) { + Thread t = new Thread(() -> { + try { + barberShopProblem.customerWalksIn(); + } catch (InterruptedException ie) { + + } + }); + set.add(t); + } + + for (Thread t : set) { + t.start(); + } + + for (Thread t : set) { + t.join(); + } + + set.clear(); + Thread.sleep(800); + + for (int i = 0; i < 5; i++) { + Thread t = new Thread(() -> { + try { + barberShopProblem.customerWalksIn(); + } catch (InterruptedException ie) { + + } + }); + set.add(t); + } + for (Thread t : set) { + t.start(); + } + + barberThread.join(); + } + + +} diff --git a/src/main/java/multithreading/educative/Barrier.java b/src/main/java/multithreading/educative/Barrier.java new file mode 100644 index 0000000..d427d6e --- /dev/null +++ b/src/main/java/multithreading/educative/Barrier.java @@ -0,0 +1,41 @@ +package multithreading.educative; + +public class Barrier { + + int count = 0; + int released = 0; + int totalThreads; + + public Barrier(int totalThreads) { + this.totalThreads = totalThreads; + } + + public synchronized void await() throws InterruptedException { + // block any new threads from proceeding till, + // all threads from previous barrier are released + while (count == totalThreads) + wait(); + + // increment the counter whenever a thread arrives at the + // barrier. + count++; + + if (count == totalThreads) { + // wake up all the threads. + notifyAll(); + // remember to set released to totalThreads + released = totalThreads; + } else { + // wait till all threads reach barrier + while (count < totalThreads) + wait(); + } + + released--; + if (released == 0) { + count = 0; + // remember to wake up any threads + notifyAll(); + } + } +} diff --git a/src/main/java/multithreading/educative/BlockingQueue.java b/src/main/java/multithreading/educative/BlockingQueue.java new file mode 100644 index 0000000..8b17411 --- /dev/null +++ b/src/main/java/multithreading/educative/BlockingQueue.java @@ -0,0 +1,113 @@ +package multithreading.educative; + +class BlockingQueue { + + T[] array; + Object lock = new Object(); + int size = 0; + int capacity; + int head = 0; + int tail = 0; + + @SuppressWarnings("unchecked") + public BlockingQueue(int capacity) { + // The casting results in a warning + array = (T[]) new Object[capacity]; + this.capacity = capacity; + } + + public T dequeue() throws InterruptedException { + + T item = null; + synchronized (lock) { + + while (size == 0) { + lock.wait(); + } + + if (head == capacity) { + head = 0; + } + + item = array[head]; + array[head] = null; + head++; + size--; + + lock.notify(); + } + + return item; + } + + public void enqueue(T item) throws InterruptedException { + + synchronized (lock) { + + while (size == capacity) { + lock.wait(); + } + + if (tail == capacity) { + tail = 0; + } + + array[tail] = item; + size++; + tail++; + lock.notify(); + } + } +} + +class Demonstration1 { + public static void main( String args[] ) throws Exception{ + final BlockingQueue q = new BlockingQueue(5); + + Thread t1 = new Thread(new Runnable() { + + @Override + public void run() { + try { + for (int i = 0; i < 50; i++) { + q.enqueue(i); + System.out.println("enqueued " + i); + } + } catch (InterruptedException ie) { + + } + } + }); + + Thread t2 = new Thread(() -> { + try { + for (int i = 0; i < 25; i++) { + System.out.println("Thread 2 dequeued: " + q.dequeue()); + } + } catch (InterruptedException ie) { + + } + }); + + Thread t3 = new Thread(() -> { + try { + for (int i = 0; i < 25; i++) { + System.out.println("Thread 3 dequeued: " + q.dequeue()); + } + } catch (InterruptedException ie) { + + } + }); + + t1.start(); + Thread.sleep(4000); + t2.start(); + + t2.join(); + + t3.start(); + t1.join(); + t3.join(); + } +} + diff --git a/src/main/java/multithreading/educative/CountingSemaphore.java b/src/main/java/multithreading/educative/CountingSemaphore.java new file mode 100644 index 0000000..4df3511 --- /dev/null +++ b/src/main/java/multithreading/educative/CountingSemaphore.java @@ -0,0 +1,29 @@ +package multithreading.educative; + +class CountingSemaphore { + + int usedPermits = 0; + int maxCount; + + public CountingSemaphore(int count) { + this.maxCount = count; + } + + public synchronized void acquire() throws InterruptedException { + + while (usedPermits == maxCount) + wait(); + + notify(); + usedPermits++; + } + + public synchronized void release() throws InterruptedException { + + while (usedPermits == 0) + wait(); + + usedPermits--; + notify(); + } +} diff --git a/src/main/java/multithreading/educative/DeferredCallbackExecutor.java b/src/main/java/multithreading/educative/DeferredCallbackExecutor.java new file mode 100644 index 0000000..702ddd4 --- /dev/null +++ b/src/main/java/multithreading/educative/DeferredCallbackExecutor.java @@ -0,0 +1,84 @@ +package multithreading.educative; + +import java.util.Comparator; +import java.util.PriorityQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; + +// Problem +// Design and implement a thread-safe class that allows registration of callback methods that are +// executed after a user specified time interval in seconds has elapsed. + +public class DeferredCallbackExecutor { + + PriorityQueue q = new PriorityQueue<>((o1, o2) -> (int) (o1.executeAt - o2.executeAt)); + + ReentrantLock lock = new ReentrantLock(); + Condition newCallbackArrived = lock.newCondition(); + +// whenever a consumer thread requests a callback be registered, the caveat is to wake up the execution thread +// and recalculate the minimum duration it needs to sleep for before the earliest callback becomes due for execution. +// Consider this example: initially, the execution thread is sleeping for 30 mins before any callback in the min-heap is due. +// A consumer thread comes along and adds a callback to be executed after 5 minutes. +// The execution thread would need to wake up and reset itself to sleep for only 5 minutes instead of 30 minutes. +// Once we find an elegant way of capturing this logic our problem is pretty much solved. + public void registerCallback(CallBack callBack) { + lock.lock(); + q.add(callBack); + newCallbackArrived.signal(); + lock.unlock(); + } + private long findSleepDuration() { + long currentTime = System.currentTimeMillis(); + return q.peek().executeAt - currentTime; + } + public void start() throws InterruptedException { + long sleepFor = 0; + while (true) { + + lock.lock(); + /** + * Initially the queue will be empty and the execution thread should just wait indefinitely on the condition variable to be signaled. + */ + while (q.isEmpty()) { + newCallbackArrived.await(); + } + + while (!q.isEmpty()) { + sleepFor = findSleepDuration(); + + if(sleepFor <=0) + return; + /** + * Now two things are possible at this point. + * No new callbacks arrive, in which case the executor thread completes waiting and + * polls the queue for tasks that should be executed and starts executing them. + * + * Or that another callback arrives, + * in which case the consumer thread will signal the condition variable newCallbackArrived to wake up the execution thread and + * have it re-evaluate the duration it can sleep for before the earliest callback becomes due. + */ + newCallbackArrived.await(sleepFor, TimeUnit.MILLISECONDS); + } + + CallBack cb = q.poll(); + System.out.println( + "Executed at " + System.currentTimeMillis()/1000 + " required at " + cb.executeAt/1000 + + ": message:" + cb.message); + + lock.unlock(); + } + } + + static class CallBack { + + long executeAt; + String message; + + public CallBack(long executeAfter, String message) { + this.executeAt = System.currentTimeMillis() + executeAfter * 1000; + this.message = message; + } + } +} diff --git a/src/main/java/multithreading/educative/DemonstrationThreadLocal.java b/src/main/java/multithreading/educative/DemonstrationThreadLocal.java new file mode 100644 index 0000000..4836852 --- /dev/null +++ b/src/main/java/multithreading/educative/DemonstrationThreadLocal.java @@ -0,0 +1,91 @@ +package multithreading.educative; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +public class DemonstrationThreadLocal { + public static void main(String args[]) throws Exception { + + usingThreads(); + usingSingleThreadPool(); + usingMultiThreadsPool(); + } + + static void usingThreads() throws Exception { + + Counter counter = new Counter(); + Thread[] tasks = new Thread[100]; + + for (int i = 0; i < 100; i++) { + Thread t = new Thread(() -> { + for (int j = 0; j < 100; j++) + counter.increment(); + }); + tasks[i] = t; + t.start(); + } + + for (int i = 0; i < 100; i++) { + tasks[i].join(); + } + + System.out.println(counter.counter.get()); + } + + @SuppressWarnings("unchecked") + static void usingSingleThreadPool() throws Exception { + + Counter counter = new Counter(); + ExecutorService es = Executors.newFixedThreadPool(1); + Future[] tasks = new Future[100]; + + for (int i = 0; i < 100; i++) { + tasks[i] = es.submit(() -> { + for (int j = 0; j < 100; j++) + counter.increment(); + + return counter.counter.get(); + }); + } + + System.out.println(tasks[99].get()); + + es.shutdown(); + } + + @SuppressWarnings("unchecked") + static void usingMultiThreadsPool() throws Exception { + + Counter counter = new Counter(); + ExecutorService es = Executors.newFixedThreadPool(20); + Future[] tasks = new Future[100]; + + for (int i = 0; i < 100; i++) { + tasks[i] = es.submit(() -> { + for (int j = 0; j < 100; j++) + counter.increment(); + + return counter.counter.get(); + }); + } + + System.out.println(tasks[99].get()); + + es.shutdown(); + } + + + static class Counter { + + ThreadLocal counter = ThreadLocal.withInitial(() -> 0); + + public Counter() { + counter.set(0); + } + + void increment() { + counter.set(counter.get() + 1); + } + } +} diff --git a/src/main/java/multithreading/educative/DiningPhilosophers.java b/src/main/java/multithreading/educative/DiningPhilosophers.java new file mode 100644 index 0000000..128b02f --- /dev/null +++ b/src/main/java/multithreading/educative/DiningPhilosophers.java @@ -0,0 +1,44 @@ +package multithreading.educative; + +import java.util.Random; +import java.util.concurrent.Semaphore; + +public class DiningPhilosophers { + + private static Random random = new Random(System.currentTimeMillis()); + + private Semaphore[] forks = new Semaphore[5]; + private Semaphore maxDiners = new Semaphore(4); + + public DiningPhilosophers() { + forks[0] = new Semaphore(1); + forks[1] = new Semaphore(1); + forks[2] = new Semaphore(1); + forks[3] = new Semaphore(1); + forks[4] = new Semaphore(1); + } + + void contemplate() throws InterruptedException { + Thread.sleep(random.nextInt(500)); + } + + void eat(int id) throws InterruptedException { + maxDiners.acquire(); + + forks[id].acquire(); + forks[(id + 1) % 5].acquire(); + System.out.println("Philosipher " + id + " is eating"); + forks[id].release(); + forks[(id + 1) % 5].release(); + + maxDiners.release(); + } + + public void lifecycleOfPhilosopher(int id) throws InterruptedException { + + while (true) { + contemplate(); + eat(id); + } + } +} diff --git a/src/main/java/multithreading/educative/DiningPhilosophers2.java b/src/main/java/multithreading/educative/DiningPhilosophers2.java new file mode 100644 index 0000000..adce6f6 --- /dev/null +++ b/src/main/java/multithreading/educative/DiningPhilosophers2.java @@ -0,0 +1,55 @@ +package multithreading.educative; + +import java.util.Random; +import java.util.concurrent.Semaphore; + +public class DiningPhilosophers2 { + + private static Random random = new Random(System.currentTimeMillis()); + + private Semaphore[] forks = new Semaphore[5]; + + public DiningPhilosophers2() { + forks[0] = new Semaphore(1); + forks[1] = new Semaphore(1); + forks[2] = new Semaphore(1); + forks[3] = new Semaphore(1); + forks[4] = new Semaphore(1); + } + + void acquireForkForRightHanded(int id) throws InterruptedException { + forks[id].acquire(); + forks[(id + 1) % 5].acquire(); + } + + void acquireForkLeftHanded(int id) throws InterruptedException { + forks[(id + 1) % 5].acquire(); + forks[id].acquire(); + } + + void contemplate() throws InterruptedException { + Thread.sleep(random.nextInt(500)); + } + + void eat(int id) throws InterruptedException { + + if (id == 3) { + acquireForkLeftHanded(3); + + } else { + acquireForkForRightHanded(id); + } + + System.out.println("Philosipher " + id + " is eating"); + forks[id].release(); + forks[(id + 1) % 5].release(); + } + + public void lifecycleOfPhilosopher(int id) throws InterruptedException { + + while (true) { + contemplate(); + eat(id); + } + } +} diff --git a/src/main/java/multithreading/educative/MultiThreadedMergeSort.java b/src/main/java/multithreading/educative/MultiThreadedMergeSort.java new file mode 100644 index 0000000..95cb878 --- /dev/null +++ b/src/main/java/multithreading/educative/MultiThreadedMergeSort.java @@ -0,0 +1,105 @@ +package multithreading.educative; + +import java.util.Random; + +public class MultiThreadedMergeSort { + + private static int SIZE = 1000; + private static Random random = new Random(System.currentTimeMillis()); + private int[] input = new int[SIZE]; + private int[] scratch = new int[SIZE]; + + private void printArray(int[] input) { + System.out.println(); + for (int i = 0; i < input.length; i++) + System.out.print(" " + input[i] + " "); + System.out.println(); + } + + private void createTestData() { + + for (int i = 0; i < SIZE; i++) { + input[i] = random.nextInt(10000); + } + } + + private void mergeSort(final int start, final int end, final int[] input) { + + if (start == end) { + return; + } + + final int mid = (start + end) / 2; + + // sort first half + Thread worker1 = new Thread(new Runnable() { + + public void run() { + mergeSort(start, mid, input); + } + }); + + // sort second half + Thread worker2 = new Thread(new Runnable() { + + public void run() { + mergeSort(mid + 1, end, input); + } + }); + + // start the threads + worker1.start(); + worker2.start(); + + try { + + worker1.join(); + worker2.join(); + } catch (InterruptedException ie) { + // swallow + } + + // merge the two sorted arrays + int i = start; + int j = mid + 1; + int k; + + for (k = start; k <= end; k++) { + scratch[k] = input[k]; + } + + k = start; + while (k <= end) { + + if (i <= mid && j <= end) { + input[k] = Math.min(scratch[i], scratch[j]); + + if (input[k] == scratch[i]) { + i++; + } else { + j++; + } + } else if (i <= mid && j > end) { + input[k] = scratch[i]; + i++; + } else { + input[k] = scratch[j]; + j++; + } + k++; + } + } + + public void test() { + createTestData(); + // int[] result = mergeSort(0, input.length - 1, input); + // printArray(input); + // printArray(result); + printArray(input); + long start = System.currentTimeMillis(); + mergeSort(0, input.length - 1, input); + long end = System.currentTimeMillis(); + System.out.println("Time taken = " + (end - start)); + printArray(input); + } +} diff --git a/src/main/java/multithreading/educative/ReadWriteLock.java b/src/main/java/multithreading/educative/ReadWriteLock.java new file mode 100644 index 0000000..a72b3c5 --- /dev/null +++ b/src/main/java/multithreading/educative/ReadWriteLock.java @@ -0,0 +1,66 @@ +package multithreading.educative; + +public class ReadWriteLock { + + boolean isWriteLocked = false; + int readers = 0; + + public synchronized void acquireReadLock() throws InterruptedException { + while (isWriteLocked) { + wait(); + } + readers++; + } + + public synchronized void acquireWriteLock() throws InterruptedException { + while (isWriteLocked || readers != 0) { + wait(); + } + isWriteLocked = true; + } + + public synchronized void releaseReadLock() { + readers--; + notify(); + } + + public synchronized void releaseWriteLock() { + isWriteLocked = false; + notify(); + } +} + +class ReadWriteLock1 { + + private int readers = 0; + private int writers = 0; + private int writeRequests = 0; + + public synchronized void lockRead() throws InterruptedException { + while (writers > 0 || writeRequests > 0) { + wait(); + } + readers++; + } + + public synchronized void unlockRead() { + readers--; + notifyAll(); + } + + public synchronized void lockWrite() throws InterruptedException { + writeRequests++; + + while (readers > 0 || writers > 0) { + wait(); + } + writeRequests--; + writers++; + } + + public synchronized void unlockWrite() { + writers--; + notifyAll(); + } +} + diff --git a/src/main/java/multithreading/educative/TokenBucketFilter.java b/src/main/java/multithreading/educative/TokenBucketFilter.java new file mode 100644 index 0000000..b02fab2 --- /dev/null +++ b/src/main/java/multithreading/educative/TokenBucketFilter.java @@ -0,0 +1,66 @@ +package multithreading.educative; + +import java.util.HashSet; +import java.util.Set; + +class Demonstration { + public static void main(String args[]) throws InterruptedException { + TokenBucketFilter.runTestMaxTokenIsTen(); + } +} + +public class TokenBucketFilter { + + long possibleTokens = 0; + private int MAX_TOKENS; + private long lastRequestTime = System.currentTimeMillis(); + + public TokenBucketFilter(int maxTokens) { + MAX_TOKENS = maxTokens; + } + + synchronized void getToken() throws InterruptedException { + + possibleTokens += (System.currentTimeMillis() - lastRequestTime) / 1000; + + if (possibleTokens > MAX_TOKENS) { + possibleTokens = MAX_TOKENS; + } + + if (possibleTokens == 0) { + Thread.sleep(1000); + } else { + possibleTokens--; + } + lastRequestTime = System.currentTimeMillis(); + + System.out.println( + "Granting " + Thread.currentThread().getName() + " token at " + System.currentTimeMillis() / 1000); + } + + + public static void runTestMaxTokenIsTen() throws InterruptedException { + Set allThreads = new HashSet<>(); + final TokenBucketFilter tokenBucketFilter = new TokenBucketFilter(5); +// Sleep for 10 seconds. + Thread.sleep(10000); +// Generate 12 threads requesting tokens almost all at once. + for (int i = 0; i < 12; i++) { + Thread thread = new Thread(() -> { + try { + tokenBucketFilter.getToken(); + } catch (InterruptedException ie) { + System.out.println("We have a problem"); + } + }); + thread.setName("Thread_" + (i + 1)); + allThreads.add(thread); + } + for (Thread t : allThreads) { + t.start(); + } + for (Thread t : allThreads) { + t.join(); + } + } +} diff --git a/src/main/java/multithreading/educative/UberSeatingProblem.java b/src/main/java/multithreading/educative/UberSeatingProblem.java new file mode 100644 index 0000000..a1e725e --- /dev/null +++ b/src/main/java/multithreading/educative/UberSeatingProblem.java @@ -0,0 +1,162 @@ +package multithreading.educative; + +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.BrokenBarrierException; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.Semaphore; +import java.util.concurrent.locks.ReentrantLock; + +public class UberSeatingProblem { + public static void main(String[] args) throws InterruptedException { + runTest(); + } + + CyclicBarrier barrier = new CyclicBarrier(4); + ReentrantLock lock = new ReentrantLock(); + private int republicans = 0; + private int democrats = 0; + private Semaphore demsWaiting = new Semaphore(0); + private Semaphore repubsWaiting = new Semaphore(0); + + void drive() { + System.out.println("Uber Ride on Its wayyyy... with ride leader " + Thread.currentThread().getName()); + } + /** + * Flow goes like this: + * step 1: let's say democrat 1 comes, it gets lock on object, increases count, and goes directly to else state, + * releases lock and checks for semaphore, + * since it's not there it will be in blocked state (ready to proceed once acquiring semaphore) + * + * step 2: let's say 3 more democrats are waiting, fourth thread comes and goes into first if block and releases 3 semaphores, + * the 3 previous waiting thread goes and wait's for current thread to join at the barrier, once it comes all goes into seating + * + * step 3: if more than 2 republicans are waiting and current democrat count is 2, this condition releases 1 democrat and 2 republican semaphore + * it releases 1 democrat because the current thread is also a democrat, and all will be waiting near the barrier till 4th comes + */ + + + void seatDemocrat() throws InterruptedException, BrokenBarrierException { + + boolean rideLeader = false; + + lock.lock(); + System.out.println("###### locked "+Thread.currentThread().getName()); + democrats++; + + if (democrats == 4) { + // Seat all the democrats in the Uber ride. + demsWaiting.release(3); + democrats -= 4; + rideLeader = true; + } else if (democrats == 2 && republicans >= 2) { + // Seat 2 democrats & 2 republicans + demsWaiting.release(1); + repubsWaiting.release(2); + rideLeader = true; + democrats -= 2; + republicans -= 2; + } else { + System.out.println("###### unlocking "+Thread.currentThread().getName() ); + lock.unlock(); + System.out.println("###### State of thread before acquire "+ Thread.currentThread().getName() +" "+Thread.currentThread().getState()); + demsWaiting.acquire(); // thread will be in blocked state not in wait state + //In the BLOCKED state, a thread is about to enter a synchronized block, + // but there is another thread currently running inside a synchronized block on the same object. + // The first thread must then wait for the second thread to exit its block. + System.out.println("###### State of thread after acquire "+ Thread.currentThread().getName() +" "+Thread.currentThread().getState()); + } + + seated(); + barrier.await(); + + if (rideLeader) { + drive(); + lock.unlock(); + } + } + + void seatRepublican() throws InterruptedException, BrokenBarrierException { + + boolean rideLeader = false; + lock.lock(); + + republicans++; + + if (republicans == 4) { + // Seat all the republicans in the Uber ride. + repubsWaiting.release(3); + rideLeader = true; + republicans -= 4; + } else if (republicans == 2 && democrats >= 2) { + // Seat 2 democrats & 2 republicans + repubsWaiting.release(1); + demsWaiting.release(2); + rideLeader = true; + republicans -= 2; + democrats -= 2; + } else { + lock.unlock(); + repubsWaiting.acquire(); + } + + seated(); + barrier.await(); + + if (rideLeader) { + drive(); + lock.unlock(); + } + } + + void seated() { + System.out.println(Thread.currentThread().getName() + " seated"); + } + + public static void runTest() throws InterruptedException { + + + final UberSeatingProblem uberSeatingProblem = new UberSeatingProblem(); + Set allThreads = new HashSet<>(); + + for (int i = 0; i < 10; i++) { + + Thread thread = new Thread(() -> { + try { + uberSeatingProblem.seatDemocrat(); + } catch (InterruptedException | BrokenBarrierException ie) { + System.out.println("We have a problem"); + + } + + }); + thread.setName("Democrat_" + (i + 1)); + allThreads.add(thread); + + Thread.sleep(50); + } + + for (int i = 0; i < 14; i++) { + Thread thread = new Thread(() -> { + try { + uberSeatingProblem.seatRepublican(); + } catch (InterruptedException | BrokenBarrierException ie) { + System.err.println("We have a problem"); + + } + }); + thread.setName("Republican_" + (i + 1)); + allThreads.add(thread); + Thread.sleep(20); + } + + for (Thread t : allThreads) { + t.start(); + } + + for (Thread t : allThreads) { + t.join(); + } + } +} + diff --git a/src/main/java/multithreading/educative/UnisexBathroom.java b/src/main/java/multithreading/educative/UnisexBathroom.java new file mode 100644 index 0000000..b27e492 --- /dev/null +++ b/src/main/java/multithreading/educative/UnisexBathroom.java @@ -0,0 +1,70 @@ +package multithreading.educative; + +import java.util.concurrent.Semaphore; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; + +public class UnisexBathroom { + + static String WOMEN = "women"; + static String MEN = "men"; + static String NONE = "none"; + + String inUseBy = NONE; + int empsInBathroom = 0; + ReentrantLock lock = new ReentrantLock(true); + Condition cond = lock.newCondition(); + Semaphore maxEmps = new Semaphore(3, true); + + void femaleUseBathroom(String name) throws InterruptedException { + + lock.lock(); + while (inUseBy.equals(MEN)) { + cond.await(); + } + maxEmps.acquire(); + empsInBathroom++; + inUseBy = WOMEN; + lock.unlock(); + + useBathroom(name); + maxEmps.release(); + + lock.lock(); + empsInBathroom--; + + if (empsInBathroom == 0) + inUseBy = NONE; + cond.signalAll(); + lock.unlock(); + } + + void maleUseBathroom(String name) throws InterruptedException { + + lock.lock(); + while (inUseBy.equals(WOMEN)) { + cond.await(); + } + maxEmps.acquire(); + empsInBathroom++; + inUseBy = MEN; + lock.unlock(); + + useBathroom(name); + maxEmps.release(); + + lock.lock(); + empsInBathroom--; + + if (empsInBathroom == 0) + inUseBy = NONE; + cond.signalAll(); + lock.unlock(); + } + + void useBathroom(String name) throws InterruptedException { + System.out.println(name + " using bathroom. Current employees in bathroom = " + empsInBathroom); + Thread.sleep(10000); + System.out.println(name + " done using bathroom"); + } +} diff --git a/src/main/java/multithreading/educative/UnisexBathroom2.java b/src/main/java/multithreading/educative/UnisexBathroom2.java new file mode 100644 index 0000000..051137b --- /dev/null +++ b/src/main/java/multithreading/educative/UnisexBathroom2.java @@ -0,0 +1,126 @@ +package multithreading.educative; + +import java.util.concurrent.Semaphore; + +public class UnisexBathroom2 { + + static String WOMEN = "women"; + static String MEN = "men"; + static String NONE = "none"; + + volatile String inUseBy = NONE; + int empsInBathroom = 0; + Semaphore maxEmps = new Semaphore(3,true); + + void femaleUseBathroom(String name) throws InterruptedException { + + synchronized (this) { + while (inUseBy.equals(MEN)) { + this.wait(); + } + maxEmps.acquire(); + empsInBathroom++; + inUseBy = WOMEN; + } + + useBathroom(name); + maxEmps.release(); + + synchronized (this) { + empsInBathroom--; + + if (empsInBathroom == 0) + inUseBy = NONE; + this.notifyAll(); + } + } + + void maleUseBathroom(String name) throws InterruptedException { + + synchronized (this) { + while (inUseBy.equals(WOMEN)) { + this.wait(); + } + maxEmps.acquire(); + empsInBathroom++; + inUseBy = MEN; + } + + useBathroom(name); + maxEmps.release(); + + synchronized (this) { + empsInBathroom--; + + if (empsInBathroom == 0) + inUseBy = NONE; + this.notifyAll(); + } + } + + void useBathroom(String name) throws InterruptedException { + System.out.println(name + " using bathroom. Current employees in bathroom = " + empsInBathroom); + Thread.sleep(10000); + System.out.println(name + " done using bathroom"); + } + + public static void runTest() throws InterruptedException { + + final UnisexBathroom2 unisexBathroom = new UnisexBathroom2(); + + Thread female1 = new Thread(() -> { + try { + unisexBathroom.femaleUseBathroom("Lisa"); + } catch (InterruptedException ie) { + + } + }); + + Thread male1 = new Thread(() -> { + + try { + unisexBathroom.maleUseBathroom("John"); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + }); + + Thread male2 = new Thread(() -> { + try { + unisexBathroom.maleUseBathroom("Bob"); + } catch (InterruptedException ie) { + + } + }); + + Thread male3 = new Thread(() -> { + try { + unisexBathroom.maleUseBathroom("Anil"); + } catch (InterruptedException ie) { + + } + }); + + Thread male4 = new Thread(() -> { + try { + unisexBathroom.maleUseBathroom("Wentao"); + } catch (InterruptedException ie) { + + } + }); + + female1.start(); + male1.start(); + male2.start(); + male3.start(); + male4.start(); + + female1.join(); + male1.join(); + male2.join(); + male3.join(); + male4.join(); + + } +} diff --git a/src/main/java/multithreading/educative/companies/netflix/Callback.java b/src/main/java/multithreading/educative/companies/netflix/Callback.java new file mode 100644 index 0000000..7b76d5f --- /dev/null +++ b/src/main/java/multithreading/educative/companies/netflix/Callback.java @@ -0,0 +1,6 @@ +package multithreading.educative.companies.netflix; + +public interface Callback { + + public void done(); +} diff --git a/src/main/java/multithreading/educative/companies/netflix/Executor.java b/src/main/java/multithreading/educative/companies/netflix/Executor.java new file mode 100644 index 0000000..2ba9354 --- /dev/null +++ b/src/main/java/multithreading/educative/companies/netflix/Executor.java @@ -0,0 +1,17 @@ +package multithreading.educative.companies.netflix; + +public class Executor { + + public void asynchronousExecution(Callback callback) throws Exception { + + Thread t = new Thread(() -> { + // Do some useful work + try { + Thread.sleep(5000); + } catch (InterruptedException ie) { + } + callback.done(); + }); + t.start(); + } +} diff --git a/src/main/java/multithreading/educative/companies/netflix/Run.java b/src/main/java/multithreading/educative/companies/netflix/Run.java new file mode 100644 index 0000000..10e9ea5 --- /dev/null +++ b/src/main/java/multithreading/educative/companies/netflix/Run.java @@ -0,0 +1,23 @@ +package multithreading.educative.companies.netflix; + +public class Run { + + public static void main(String[] args) throws Exception { + + SynchronousExecutor executor = new SynchronousExecutor(); + executor.asynchronousExecution(() -> { + System.out.println("I am done"); + }); + + System.out.println("main thread exiting..."); + } + + void runAsynchronously() throws Exception { + Executor executor = new Executor(); + executor.asynchronousExecution(() -> { + System.out.println("I am done"); + }); + + System.out.println("main thread exiting..."); + } +} diff --git a/src/main/java/multithreading/educative/companies/netflix/SynchronousExecutor.java b/src/main/java/multithreading/educative/companies/netflix/SynchronousExecutor.java new file mode 100644 index 0000000..5216bc6 --- /dev/null +++ b/src/main/java/multithreading/educative/companies/netflix/SynchronousExecutor.java @@ -0,0 +1,33 @@ +package multithreading.educative.companies.netflix; + +public class SynchronousExecutor extends Executor { + + @Override + public void asynchronousExecution(Callback callback) throws Exception { + + Object signal = new Object(); + final boolean[] isDone = new boolean[1]; + + Callback cb = new Callback() { + + @Override + public void done() { + callback.done(); + synchronized (signal) { + signal.notify(); + isDone[0] = true; + } + } + }; + + // Call the asynchronous executor + super.asynchronousExecution(cb); + + synchronized (signal) { + while (!isDone[0]) { + signal.wait(); + } + } + + } +} diff --git a/src/main/java/multithreading/educative/examples/CallableExample.java b/src/main/java/multithreading/educative/examples/CallableExample.java new file mode 100644 index 0000000..e4d2ac2 --- /dev/null +++ b/src/main/java/multithreading/educative/examples/CallableExample.java @@ -0,0 +1,117 @@ +package multithreading.educative.examples; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +public class CallableExample { + + int example1(final int n) throws ExecutionException, InterruptedException { + + ExecutorService threadPool = Executors.newFixedThreadPool(5); + + Callable sumTask = new Callable() { + + public Integer call() throws Exception { + int sum = 0; + for (int i = 1; i <= n; i++) + sum += i; + System.out.println("Running"); + return sum; + } + }; + + Future f = threadPool.submit(sumTask); + int ans = f.get(); + + + f = threadPool.submit(sumTask); + f.get(); + + return ans; + } + + int example2(final int n) throws ExecutionException, InterruptedException { + + ExecutorService threadPool = Executors.newFixedThreadPool(5); + int result = -1; + + Callable sumTask = new Callable() { + + public Integer call() throws Exception { + throw new RuntimeException("something bad happened."); + } + }; + + Future f = threadPool.submit(sumTask); + + try { + result = f.get(); + } catch (ExecutionException ee) { + System.out.println("Something went wrong."); + } + + return result; + } + + int example3(final int n) throws ExecutionException, InterruptedException { + + ExecutorService threadPool = Executors.newSingleThreadExecutor(); + int result = -1; + + Callable sumTask1 = new Callable() { + + public Integer call() throws Exception { + + // wait for 2 seconds + Thread.sleep(1000); + + int sum = 0; + for (int i = 1; i <= n; i++) + sum += i; + return sum; + } + }; + + Callable randomTask = new Callable() { + + public Void call() throws Exception { + + // go to sleep for an hours + Thread.sleep(3600 * 1000); + return null; + } + }; + + Future f1 = threadPool.submit(sumTask1); + Future f2 = threadPool.submit(randomTask); + + // Poll for completion of first task + try { + + // Before we poll for completion of second task, + // cancel the second one + f2.cancel(true); + + while (!f1.isDone()) { + System.out.println("Waiting for first task to complete."); + } + result = f1.get(); + } catch (ExecutionException ee) { + System.out.println("Something went wrong."); + } + + System.out.println("Is second task cancelled : " + f2.isCancelled()); + + return result; + } + + void test() throws ExecutionException, InterruptedException { + // System.out.println(example1(10)); + + System.out.println(example1(10)); + } + +} diff --git a/src/main/java/multithreading/educative/examples/DaemonThreadSpawn.java b/src/main/java/multithreading/educative/examples/DaemonThreadSpawn.java new file mode 100644 index 0000000..bb43fa3 --- /dev/null +++ b/src/main/java/multithreading/educative/examples/DaemonThreadSpawn.java @@ -0,0 +1,21 @@ +package multithreading.educative.examples; + +public class DaemonThreadSpawn { + + public void spawnDaemonThread() { + + Thread innerThread = new Thread(new Runnable() { + + public void run() { + + for (int i = 0; i < 100; i++) { + System.out.println("I am a daemon thread !"); + } + } + }); + + innerThread.setDaemon(true); + innerThread.start(); + System.out.println("Main thread exiting"); + } +} diff --git a/src/main/java/multithreading/educative/examples/FutureTaskExample.java b/src/main/java/multithreading/educative/examples/FutureTaskExample.java new file mode 100644 index 0000000..42e25af --- /dev/null +++ b/src/main/java/multithreading/educative/examples/FutureTaskExample.java @@ -0,0 +1,89 @@ +package multithreading.educative.examples; + +import java.util.Random; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorCompletionService; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.FutureTask; + +public class FutureTaskExample { + + Random random = new Random(System.currentTimeMillis()); + + @SuppressWarnings("") + void example() throws Exception { + + FutureTask futureTask = new FutureTask(new Callable() { + + public Object call() throws Exception { + try{ + Thread.sleep(1); + } + catch(InterruptedException ie){ + // swallow exception + } + return 5; + } + }); + + ExecutorService threadPool = (Executors.newSingleThreadExecutor()); + Future duplicateFuture = threadPool.submit(futureTask); + + // Awful idea to busy wait + while (!futureTask.isDone()) { + System.out.println("Waiting"); + } + + if(duplicateFuture.isDone() != futureTask.isDone()){ + System.out.println("This should never happen."); + } + + System.out.println(futureTask.get()); + } + + void completionServiceExample() throws Exception { + + class TrivialTask implements Runnable { + + int n; + + public TrivialTask(int n) { + this.n = n; + } + + public void run() { + try { + // sleep for one second + Thread.sleep(random.nextInt(101)); + } catch (InterruptedException ie) { + // swallow exception + } + } + } + + ExecutorService threadPool = Executors.newFixedThreadPool(3); + ExecutorCompletionService service = + new ExecutorCompletionService(threadPool); + + // Submit 10 trivial tasks. + for (int i = 0; i < 10; i++) { + service.submit(new TrivialTask(i), (i)); + } + + // wait for all tasks to get done + int count = 10; + while (count != 0) { + Future f = service.poll(); + if (f != null) { + System.out.println("Thread" + f.get() + " got done."); + count--; + } + } + + threadPool.shutdown(); + + } + +} diff --git a/src/main/java/multithreading/educative/examples/StockOrder.java b/src/main/java/multithreading/educative/examples/StockOrder.java new file mode 100644 index 0000000..914554e --- /dev/null +++ b/src/main/java/multithreading/educative/examples/StockOrder.java @@ -0,0 +1,47 @@ +package multithreading.educative.examples; + +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +public class StockOrder { + + class Order { + void execute() {} + } + + Order waitForNextOrder() {return null;} + + void receiveAndExecuteClientOrders() { + + while (true) { + Order order = waitForNextOrder(); + order.execute(); + } + } + + void receiveAndExecuteClientOrdersBetter() { + + while (true) { + final Order order = waitForNextOrder(); + + Thread thread = new Thread(() -> order.execute()); + + thread.start(); + } + } + + void receiveAndExecuteClientOrdersBest() { + + int expectedConcurrentOrders = 100; + Executor executor = Executors.newFixedThreadPool(expectedConcurrentOrders); + ((ExecutorService) executor).shutdown(); + ExecutorService s = Executors.newFixedThreadPool(5); + + while (true) { + final Order order = waitForNextOrder(); + + executor.execute(() -> order.execute()); + } + } +} diff --git a/src/main/java/multithreading/educative/examples/ThreadExample.java b/src/main/java/multithreading/educative/examples/ThreadExample.java new file mode 100644 index 0000000..3b72914 --- /dev/null +++ b/src/main/java/multithreading/educative/examples/ThreadExample.java @@ -0,0 +1,41 @@ +package multithreading.educative.examples; + +public class ThreadExample { + + class ExecuteMe implements Runnable { + + public void run() { + while (true) { + System.out.println("Say Hello over and over again."); + try { + Thread.sleep(1000); + } catch (InterruptedException ie) { + // swallow interrupted exception + + } + } + } + } + + class MyTask extends Thread { + + @Override + public void run() { + System.out.println("Hello World."); + } + + } + + public void main() throws InterruptedException { +// ExecuteMe executeMe = new ExecuteMe(); +// Thread innerThread = new Thread(executeMe); +// // innerThread.setDaemon(true); +// innerThread.start(); +// innerThread.join(); +// System.out.println("Main thread exits"); + + MyTask myTask = new MyTask(); + myTask.start(); + + } +} diff --git a/src/main/java/multithreading/educative/examples/ThreadExecutorExample.java b/src/main/java/multithreading/educative/examples/ThreadExecutorExample.java new file mode 100644 index 0000000..316b036 --- /dev/null +++ b/src/main/java/multithreading/educative/examples/ThreadExecutorExample.java @@ -0,0 +1,29 @@ +package multithreading.educative.examples; + +import java.util.concurrent.Executor; + +public class ThreadExecutorExample { + + public void main() throws InterruptedException { + + DumbExecutor dumbExecutor = new DumbExecutor(); + MyTask myTask = new MyTask(); + dumbExecutor.execute(myTask); + } + + static class MyTask implements Runnable { + + public void run() { + System.out.println("Mytask is running now ..."); + } + } + + static class DumbExecutor implements Executor { + + public void execute(Runnable runnable) { + Thread newThread = new Thread(runnable); + newThread.start(); + } + } + +} diff --git a/src/main/java/multithreading/educative/examples/ThreadInterruptedException.java b/src/main/java/multithreading/educative/examples/ThreadInterruptedException.java new file mode 100644 index 0000000..1583050 --- /dev/null +++ b/src/main/java/multithreading/educative/examples/ThreadInterruptedException.java @@ -0,0 +1,29 @@ +package multithreading.educative.examples; + +public class ThreadInterruptedException { + + class ExecuteMe implements Runnable { + + public void run() { + try { + // sleep for a thousand minutes + System.out.println("innerThread goes to sleep at " + System.currentTimeMillis() / 1000); + Thread.sleep(1000 * 1000); + } catch (InterruptedException ie) { + System.out.println("innerThread interrupted at " + +System.currentTimeMillis() / 1000); + } + } + } + + public void main() throws InterruptedException { + ExecuteMe executeMe = new ExecuteMe(); + Thread innerThread = new Thread(executeMe); + innerThread.start(); + + // Interrupt innerThread after waiting for 5 seconds + System.out.println("Main thread sleeping at " + +System.currentTimeMillis() / 1000); + Thread.sleep(5000); + innerThread.interrupt(); + System.out.println("Main thread exiting at " + +System.currentTimeMillis() / 1000); + } +} diff --git a/src/main/java/multithreading/educative/examples/ThreadSleepExample.java b/src/main/java/multithreading/educative/examples/ThreadSleepExample.java new file mode 100644 index 0000000..ddc82bf --- /dev/null +++ b/src/main/java/multithreading/educative/examples/ThreadSleepExample.java @@ -0,0 +1,24 @@ +package multithreading.educative.examples; + +public class ThreadSleepExample { + + class ExecuteMe implements Runnable { + + public void run() { + System.out.println("Hello. innerThread going to sleep"); + try { + Thread.sleep(1000); + } catch (InterruptedException ie) { + // swallow interrupted exception + } + } + } + + public void main() throws InterruptedException { + ExecuteMe executeMe = new ExecuteMe(); + Thread innerThread = new Thread(executeMe); + innerThread.start(); + innerThread.join(); + System.out.println("Main thread exiting."); + } +} diff --git a/src/main/java/multithreading/educative/examples/ThreadSpawn.java b/src/main/java/multithreading/educative/examples/ThreadSpawn.java new file mode 100644 index 0000000..d0c4d51 --- /dev/null +++ b/src/main/java/multithreading/educative/examples/ThreadSpawn.java @@ -0,0 +1,20 @@ +package multithreading.educative.examples; + +public class ThreadSpawn { + + public void spawnThread() { + + Thread innerThread = new Thread(new Runnable() { + + public void run() { + + for (int i = 0; i < 100; i++) { + System.out.println("I am a new thread !"); + } + } + }); + + innerThread.start(); + System.out.println("Main thread exiting"); + } +} diff --git a/src/main/java/multithreading/educative/examples/TimerVsPool.java b/src/main/java/multithreading/educative/examples/TimerVsPool.java new file mode 100644 index 0000000..7752c0c --- /dev/null +++ b/src/main/java/multithreading/educative/examples/TimerVsPool.java @@ -0,0 +1,64 @@ +package multithreading.educative.examples; + +import java.util.Timer; +import java.util.TimerTask; + +public class TimerVsPool { + + void timer() throws Exception { + + Timer timer = new Timer(); + TimerTask badTask = new TimerTask() { + + @Override + public void run() { + + // run forever + while (true); + + } + }; + + TimerTask goodTask = new TimerTask() { + + @Override + public void run() { + + System.out.println("Hello I am a well-behaved task"); + + } + }; + + timer.schedule(badTask, 100); + timer.schedule(goodTask, 500); + + // By three seconds, both tasks are expected to have launched + Thread.sleep(3000); + } + + void timer2() throws Exception { + + Timer timer = new Timer(); + TimerTask badTask = new TimerTask() { + + @Override + public void run() { + throw new RuntimeException("Something Bad Happened"); + } + }; + + TimerTask goodTask = new TimerTask() { + + @Override + public void run() { + System.out.println("Hello I am a well-behaved task"); + } + }; + + timer.schedule(badTask, 10); + Thread.sleep(500); + timer.schedule(goodTask, 10); + } + + +} diff --git a/src/main/java/multithreading/educative/superman/Superman.java b/src/main/java/multithreading/educative/superman/Superman.java new file mode 100644 index 0000000..14afed9 --- /dev/null +++ b/src/main/java/multithreading/educative/superman/Superman.java @@ -0,0 +1,28 @@ +package multithreading.educative.superman; + +public class Superman { + + private static volatile Superman superman; + + private Superman() { + + } + + public static Superman getInstance() { + + if (superman == null) { + synchronized (SupermanSlightlyBetter.class) { + + if (superman == null) { + superman = new Superman(); + } + } + } + + return superman; + } + + public void fly() { + System.out.println("I am Superman & I can fly !"); + } +} diff --git a/src/main/java/multithreading/educative/superman/SupermanNaiveButCorrect.java b/src/main/java/multithreading/educative/superman/SupermanNaiveButCorrect.java new file mode 100644 index 0000000..9acc0ed --- /dev/null +++ b/src/main/java/multithreading/educative/superman/SupermanNaiveButCorrect.java @@ -0,0 +1,20 @@ +package multithreading.educative.superman; + +public class SupermanNaiveButCorrect { + + // We are initializing the object inline + private static SupermanNaiveButCorrect superman = new SupermanNaiveButCorrect(); + + // We have marked the constructor private + private SupermanNaiveButCorrect() { + } + + public static SupermanNaiveButCorrect getInstance() { + return superman; + } + + // Object method + public void fly() { + System.out.println("I am Superman & I can fly !"); + } +} diff --git a/src/main/java/multithreading/educative/superman/SupermanSlightlyBetter.java b/src/main/java/multithreading/educative/superman/SupermanSlightlyBetter.java new file mode 100644 index 0000000..82527bc --- /dev/null +++ b/src/main/java/multithreading/educative/superman/SupermanSlightlyBetter.java @@ -0,0 +1,36 @@ +package multithreading.educative.superman; + +public class SupermanSlightlyBetter { + + private static SupermanSlightlyBetter superman; + + private SupermanSlightlyBetter() { + + } + + public static SupermanSlightlyBetter getInstance() { + + // Check if object is uninitialized + if (superman == null) { + + // Now synchronize on the class object, so that only + // 1 thread gets a chance to initialize the superman + // object. Note that multiple threads can actually find + // the superman object to be null and fall into the + // first if clause + synchronized (SupermanSlightlyBetter.class) { + + // Must check once more if the superman object is still + // null. It is possible that another thread might have + // intialized it already as multiple thread could have + // made past the first if check. + if (superman == null) { + superman = new SupermanSlightlyBetter(); + } + } + + } + + return superman; + } +} diff --git a/src/main/java/multithreading/educative/superman/SupermanWithFlaws.java b/src/main/java/multithreading/educative/superman/SupermanWithFlaws.java new file mode 100644 index 0000000..497dde2 --- /dev/null +++ b/src/main/java/multithreading/educative/superman/SupermanWithFlaws.java @@ -0,0 +1,30 @@ +package multithreading.educative.superman; + +public class SupermanWithFlaws { + + private static SupermanWithFlaws superman; + + private SupermanWithFlaws() { + + } + + // This will fail with multiple threads + public static SupermanWithFlaws getInstance() { + if (superman == null) { + // A thread can be context switched at this point and + // superman will evaluate to null for any other threads + // testing the if condition. Now multiple threads will + // fall into this if clause till the superman object is + // assigned a value. All these threads will intialize the + // superman object when it should have been initialized + // only one. + superman = new SupermanWithFlaws(); + } + return superman; + } + + // Object method + public void fly() { + System.out.println("I am Superman & I can fly !"); + } +} diff --git a/src/main/java/multithreading/practice/Addition.java b/src/main/java/multithreading/practice/Addition.java new file mode 100644 index 0000000..672430d --- /dev/null +++ b/src/main/java/multithreading/practice/Addition.java @@ -0,0 +1,52 @@ +package multithreading.practice; + + +class Demonstration { + public static void main( String args[] ) throws InterruptedException { + SumUpExample.runTest(); + } +} +class SumUpExample { + long startRange; + long endRange; + long counter = 0; + static long MAX_NUM = Integer.MAX_VALUE; + public SumUpExample(long startRange, long endRange) { + this.startRange = startRange; + this.endRange = endRange; + } + public void add() { + for (long i = startRange; i <= endRange; i++) { + counter += i; + } + } + static public void twoThreads() throws InterruptedException { + long start = System.currentTimeMillis(); + SumUpExample s1 = new SumUpExample(1, MAX_NUM / 2); + SumUpExample s2 = new SumUpExample(1 + (MAX_NUM / 2), MAX_NUM); + Thread t1 = new Thread(() -> { + s1.add(); + }); + Thread t2 = new Thread(() -> { + s2.add(); + }); + t1.start(); + t2.start(); + t1.join(); + t2.join(); + long finalCount = s1.counter + s2.counter; + long end = System.currentTimeMillis(); + System.out.println("Two threads final count = " + finalCount + " took " + (end - start)); + } + static public void oneThread() { + long start = System.currentTimeMillis(); + SumUpExample s = new SumUpExample(1, MAX_NUM ); + s.add(); + long end = System.currentTimeMillis(); + System.out.println("Single thread final count = " + s.counter + " took " + (end- start)); + } + public static void runTest() throws InterruptedException { + oneThread(); + twoThreads(); + } +} \ No newline at end of file diff --git a/src/main/java/multithreading/practice/CountdownLatch.java b/src/main/java/multithreading/practice/CountdownLatch.java new file mode 100644 index 0000000..4f13ea2 --- /dev/null +++ b/src/main/java/multithreading/practice/CountdownLatch.java @@ -0,0 +1,38 @@ +package multithreading.practice; + +import java.util.concurrent.CountDownLatch; + +public class CountdownLatch { + + public static void main(String[] args) throws InterruptedException { + final CountDownLatch countdown = new CountDownLatch(10); + for (int i = 0; i < 10; ++i) { + Thread t = new Thread(() -> { + try { + doSomething(); + countdown.countDown(); + System.out.printf("Waiting on %d other threads. Thread name %s", countdown.getCount(),Thread.currentThread().getName()); + System.out.println(); + //waits until everyone reaches this point + + } catch (InterruptedException e) { + e.printStackTrace(); + } + finish(); + }); + t.start(); + } + countdown.await(); + System.out.println("Main Thread finishing"); + } + + public static void doSomething() throws InterruptedException { + Thread.sleep(10000); + } + + public static void finish() { + System.out.println("Finished everything"); + } +} + + diff --git a/src/main/java/multithreading/practice/DiningPhilosophers.java b/src/main/java/multithreading/practice/DiningPhilosophers.java new file mode 100644 index 0000000..9ce01d4 --- /dev/null +++ b/src/main/java/multithreading/practice/DiningPhilosophers.java @@ -0,0 +1,39 @@ +package multithreading.practice; + +import java.util.concurrent.Semaphore; + +class DiningPhilosophers { + + Semaphore[] forks = new Semaphore[5]; + Semaphore eating = new Semaphore(4); + + public DiningPhilosophers() { + + for (int i = 0; i < 5; i++) { + forks[i] = new Semaphore(1); + } + + } + + // call the run() method of any runnable to execute its code + public void wantsToEat(int i, + Runnable pickLeftFork, + Runnable pickRightFork, + Runnable eat, + Runnable putLeftFork, + Runnable putRightFork) throws InterruptedException { + + eating.acquire(); + forks[i].acquire(); + forks[(i + 1) % 5].acquire(); + pickLeftFork.run(); + pickRightFork.run(); + + eat.run(); + putLeftFork.run(); + putRightFork.run(); + forks[i].release(); + forks[(i + 1) % 5].release(); + eating.release(); + } +} \ No newline at end of file diff --git a/src/main/java/multithreading/practice/FizzBuzz.java b/src/main/java/multithreading/practice/FizzBuzz.java new file mode 100644 index 0000000..a23daf9 --- /dev/null +++ b/src/main/java/multithreading/practice/FizzBuzz.java @@ -0,0 +1,62 @@ +package multithreading.practice; + +import java.util.concurrent.Semaphore; +import java.util.function.IntConsumer; + +class FizzBuzz { + private int n; + Semaphore number= new Semaphore(1); + Semaphore fizz= new Semaphore(0); + Semaphore buzz= new Semaphore(0); + Semaphore fizzbuzz= new Semaphore(0); + + public FizzBuzz(int n) { + this.n = n; + } + + // printFizz.run() outputs "fizz". + public void fizz(Runnable printFizz) throws InterruptedException { + for(int i=3;i<=n;i=i+3){ + fizz.acquire(); + if((i+3)%5==0) i=i+3; + printFizz.run(); + number.release(); + } +} + + // printBuzz.run() outputs "buzz". + public void buzz(Runnable printBuzz) throws InterruptedException { + for(int i=5;i<=n;i=i+5){ + buzz.acquire(); + if((i+5)%3==0) i=i+5; + printBuzz.run(); + number.release(); + } + + + } + + // printFizzBuzz.run() outputs "fizzbuzz". + public void fizzbuzz(Runnable printFizzBuzz) throws InterruptedException { + for(int i=15;i<=n;i=i+15){ + fizzbuzz.acquire(); + printFizzBuzz.run(); + number.release(); + } + + } + + // printNumber.accept(x) outputs "x", where x is an integer. + public void number(IntConsumer printNumber) throws InterruptedException { + for(int i=1;i<=n;i++){ + number.acquire(); + if(i%3==0 && i%5==0) fizzbuzz.release(); + else if(i%3==0) fizz.release(); + else if(i%5==0) buzz.release(); + else { + printNumber.accept(i); + number.release(); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/multithreading/practice/Foo.java b/src/main/java/multithreading/practice/Foo.java new file mode 100644 index 0000000..920504e --- /dev/null +++ b/src/main/java/multithreading/practice/Foo.java @@ -0,0 +1,121 @@ +package multithreading.practice; + + +import java.util.concurrent.Semaphore; + +/** + * "Semaphore is a bowl of marbles" - Professor Stark + *

+ * Semaphore is a bowl of marbles (or locks in this case). + * If you need a marble, and there are none, you wait. You wait until there is one marble and then you take it. + * If you release(), you will add one marble to the bowl (from thin air). + * If you release(100), you will add 100 marbles to the bowl. run2.release(); will add one "run2" marble to the "run2 bowl". + * The thread calling third() will wait until the end of second() when it releases a 'run3' marble. + * The second() will wait until the end of **first() **when it releases a 'run2' marble. + * Since first() never acquires anything, it will never wait. There is a forced wait ordering. + * With semaphores, you can start out with 1 marble or 0 marbles or 100 marbles. + * A thread can take marbles (up until it's empty) or put many marbles at a time. + **/ +class Foo { + Semaphore first = new Semaphore(0); + Semaphore second = new Semaphore(0); + Semaphore third = new Semaphore(0); + + public Foo() { + + } + + public void first(Runnable printFirst) throws InterruptedException { + first.acquire(); + printFirst.run(); + second.release(); + } + + public void second(Runnable printSecond) throws InterruptedException { + first.release(); + second.acquire(); + printSecond.run(); + third.release(); + } + + public void third(Runnable printThird) throws InterruptedException { + first.release(); + third.acquire(); + printThird.run(); + + + } + + private boolean printOneCompleted = false; + private boolean printTwoCompleted = false; + private final Object lock = new Object(); + + public void first(Runnable printFirst, Void tempToAvoidCompilationIssue) throws InterruptedException { + synchronized(lock) { + printFirst.run(); + printOneCompleted = true; + lock.notifyAll(); + } + } + + public synchronized void second(Runnable printSecond, Void tempToAvoidCompilationIssue) throws InterruptedException { + synchronized(lock) { + while (!printOneCompleted) { + lock.wait(); + } + printSecond.run(); + printTwoCompleted = true; + lock.notifyAll(); + } + } + + public synchronized void third(Runnable printThird, Void tempToAvoidCompilationIssue) throws InterruptedException { + synchronized(lock) { + while (!printTwoCompleted) { + lock.wait(); + } + printThird.run(); + } + } + +} + +class PrintInOrder { + private Thread createThread(Foo foo, int index) { + return new Thread(() -> { + try { + switch (index) { + case 1: + foo.first(() -> System.out.print("first")); + break; + case 2: + foo.second(() -> System.out.print("second")); + break; + case 3: + foo.third(() -> System.out.print("third")); + break; + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + }); + } + + public void test0() throws InterruptedException { + Foo foo = new Foo(); + int[] input = {1, 2, 3}; + for (int i : input) { + Thread thread = createThread(foo, i); + thread.start(); + } + } + + public void test1() throws InterruptedException { + Foo foo = new Foo(); + int[] input = {1, 3, 2}; + for (int i : input) { + Thread thread = createThread(foo, i); + thread.start(); + } + } +} \ No newline at end of file diff --git a/src/main/java/multithreading/practice/FooBar.java b/src/main/java/multithreading/practice/FooBar.java new file mode 100644 index 0000000..424d827 --- /dev/null +++ b/src/main/java/multithreading/practice/FooBar.java @@ -0,0 +1,32 @@ +package multithreading.practice; + +import java.util.concurrent.Semaphore; + +class FooBar { + private int n; + Semaphore one= new Semaphore(0); + Semaphore two= new Semaphore(1); + public FooBar(int n) { + this.n = n; + } + + public void foo(Runnable printFoo) throws InterruptedException { + + for (int i = 0; i < n; i++) { + + two.acquire(); + printFoo.run(); + one.release(); + } + } + + public void bar(Runnable printBar) throws InterruptedException { + + for (int i = 0; i < n; i++) { + + one.acquire(); + printBar.run(); + two.release(); + } + } +} \ No newline at end of file diff --git a/src/main/java/multithreading/practice/H2O.java b/src/main/java/multithreading/practice/H2O.java new file mode 100644 index 0000000..757323e --- /dev/null +++ b/src/main/java/multithreading/practice/H2O.java @@ -0,0 +1,27 @@ +package multithreading.practice; + +import java.util.concurrent.Semaphore; + +public class H2O { + + Semaphore s1 = new Semaphore(2); + Semaphore s2 = new Semaphore(0); + + public H2O() { + + } + + public void hydrogen(Runnable releaseHydrogen) throws InterruptedException { + s1.acquire(); + // releaseHydrogen.run() outputs "H". Do not change or remove this line. + releaseHydrogen.run(); + s2.release(); + } + + public void oxygen(Runnable releaseOxygen) throws InterruptedException { + s2.acquire(2); + // releaseOxygen.run() outputs "O". Do not change or remove this line. + releaseOxygen.run(); + s1.release(2); + } +} diff --git a/src/main/java/multithreading/practice/NonReentrantLock.java b/src/main/java/multithreading/practice/NonReentrantLock.java new file mode 100644 index 0000000..fbb1c0d --- /dev/null +++ b/src/main/java/multithreading/practice/NonReentrantLock.java @@ -0,0 +1,36 @@ +package multithreading.practice; + +class NonReentrantLock { + + boolean isLocked = false; + + public synchronized void lock() throws InterruptedException { + while (isLocked) { + wait(); + } + isLocked = true; + } + + public synchronized void unlock() { + isLocked = false; + notify(); + } + + + public NonReentrantLock() { + isLocked = false; + } + + public static void main(String args[]) throws Exception { + NonReentrantLock nreLock = new NonReentrantLock(); + + // First locking would be successful + nreLock.lock(); + System.out.println("Acquired first lock"); + + // Second locking results in a self deadlock + System.out.println("Trying to acquire second lock"); + nreLock.lock(); + System.out.println("Acquired second lock"); + } +} \ No newline at end of file diff --git a/src/main/java/multithreading/practice/OddEven.java b/src/main/java/multithreading/practice/OddEven.java new file mode 100644 index 0000000..362bb42 --- /dev/null +++ b/src/main/java/multithreading/practice/OddEven.java @@ -0,0 +1,78 @@ +package multithreading.practice; + +import java.util.concurrent.Callable; + +public class OddEven { + + public static void main(String[] args) { + + Printer p= new Printer(); + Thread odd= new Thread(new Odd(p)); + Thread even= new Thread(new Even(p)); + + odd.start(); + even.start(); + + } + + +} + +class Even implements Runnable{ + Printer print; + + public Even(Printer p) { + this.print=p; + } + + @Override + public void run() { + try { + print.printEven(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } +} + class Odd implements Runnable{ + + Printer print; + + public Odd(Printer p) { + this.print=p; + } + + @Override + public void run() { + try { + print.printOdd(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } +} + class Printer { + boolean isOdd; + int max=20; + int start=1; + + public synchronized void printEven() throws InterruptedException { + while(start<=max){ + if(start%2!=0){ + wait(); + } + System.out.println("From thread "+Thread.currentThread().getName()+" value "+start++); + notify(); + } + } + + public synchronized void printOdd() throws InterruptedException { + while(start<=max){ + if(start%2==0){ + wait(); + } + System.out.println("From thread "+Thread.currentThread().getName()+" value "+start++); + notify(); + } + } +} diff --git a/src/main/java/multithreading/practice/OddEvenSemaphore.java b/src/main/java/multithreading/practice/OddEvenSemaphore.java new file mode 100644 index 0000000..d16518e --- /dev/null +++ b/src/main/java/multithreading/practice/OddEvenSemaphore.java @@ -0,0 +1,94 @@ +package multithreading.practice; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Semaphore; + +public class OddEvenSemaphore { + public static void main(String[] args) { + //Odd odd= new Odd() + PrinterSemaphore print= new PrinterSemaphore(); +// Thread even= new Thread(new EvenSemaphore(print)); +// even.setName("From Even Thread"); +// Thread odd= new Thread(new OddSemaphore(print)); +// odd.setName("From odd Thread"); +// odd.start(); +// even.start(); + + + ExecutorService service= Executors.newFixedThreadPool(10); + service.submit(new OddSemaphore(print)); + service.submit(new EvenSemaphore(print)); + } +} + +class OddSemaphore implements Callable { + + PrinterSemaphore print; + + public OddSemaphore(PrinterSemaphore p) { + this.print=p; + } + + @Override + public Boolean call() throws Exception { + for(int i=1;i<20;i+=2){ + print.printOdd(i); + } + return true; + } +} + +class EvenSemaphore implements Callable{ + + PrinterSemaphore print; + + public EvenSemaphore(PrinterSemaphore p) { + this.print=p; + } + + @Override + public Boolean call() throws Exception { + for(int i=2;i<20;i+=2){ + try { + print.printEven(i); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + return true; + } +} + +class PrinterSemaphore { + Semaphore semOdd= new Semaphore(1); + Semaphore semEven= new Semaphore(0); + + public void printEven(int val) throws InterruptedException { + try{ + semEven.acquire(); + System.out.println(Thread.currentThread().getName()+" value :"+val); + } finally { + semOdd.release(); + } + + + } + + public void printOdd(int val){ + try { + semOdd.acquire(); + System.out.println(Thread.currentThread().getName()+" value :"+val); + } catch (InterruptedException e) { + e.printStackTrace(); + }finally { + semEven.release(); + } + + + + + } +} \ No newline at end of file diff --git a/src/main/java/multithreading/practice/ProducerConsumer.java b/src/main/java/multithreading/practice/ProducerConsumer.java new file mode 100644 index 0000000..e8ad8aa --- /dev/null +++ b/src/main/java/multithreading/practice/ProducerConsumer.java @@ -0,0 +1,116 @@ +package multithreading.practice; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.*; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +public class ProducerConsumer { + + + public static void main(String[] args) { + + + Lock lock = new ReentrantLock(); + Condition producerCond1 = lock.newCondition(); + Condition consumerCond1 = lock.newCondition(); + + List buffer1 = new ArrayList<>(20); + + class Producer implements Callable { + List buffer; + Condition producerCond; Condition consumerCond; + public Producer( List buffer, Condition producerCond, Condition consumerCond){ + this.buffer=buffer; + this.producerCond=producerCond; + this.consumerCond=consumerCond; + } + public String call() throws InterruptedException { + int count = 0; + try { + lock.lock(); + while (count++ < 50) { + + while (this.buffer.size() > 20) { + producerCond.await(); + } + this.buffer.add((int) (Math.random() * 10)); + consumerCond.signalAll(); + } + } + finally { + lock.unlock(); + } + return "Produced " + (count-1); + } + } + + class Consumer implements Callable { + List buffer; + Condition producerCond; Condition consumerCond; + public Consumer( List buffer, Condition producerCond, Condition consumerCond){ + this.buffer=buffer; + this.producerCond=producerCond; + this.consumerCond=consumerCond; + } + public String call() throws InterruptedException { + int count=0; + try { + lock.lock(); + + while (count++ <50) { + + while (this.buffer.isEmpty()) { + consumerCond.await(); + } + this.buffer.remove(buffer.size() - 1); + producerCond.signalAll(); + + } + } + finally { + lock.unlock(); + } + return "consumer " + (count-1); + } + } + + ExecutorService service = Executors.newFixedThreadPool(8); + + ExecutorService service1 = Executors.newCachedThreadPool(); + + + List> listProds = new ArrayList<>(); + List> listCons = new ArrayList<>(); + for (int i = 0; i < 4; i++) { + listProds.add(new Producer(buffer1,producerCond1,consumerCond1)); + } + + for (int i = 0; i < 4; i++) { + listCons.add(new Consumer(buffer1,producerCond1,consumerCond1)); + } + + List> allCalls = new ArrayList<>(); + allCalls.addAll(listProds); + allCalls.addAll(listCons); + + try { + List> future = service.invokeAll(allCalls); + + future.forEach(e -> { + + try { + System.out.println("result:: " + e.get()); + } catch (ExecutionException | InterruptedException ex) { + System.out.println("error "+ex.getMessage()); + } + }); + + }catch ( InterruptedException ex ){ + System.out.println("error "+ex.getMessage()); + }finally { + service.shutdown(); + } + } +} diff --git a/src/main/java/multithreading/practice/RealTimeCounter.java b/src/main/java/multithreading/practice/RealTimeCounter.java new file mode 100644 index 0000000..c8f3682 --- /dev/null +++ b/src/main/java/multithreading/practice/RealTimeCounter.java @@ -0,0 +1,99 @@ +package multithreading.practice; + +import java.util.Random; +import java.util.Timer; +import java.util.TimerTask; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicLongArray; + +public class RealTimeCounter { + + private final static int GRANULARITY = 300; + private AtomicLongArray counter = new AtomicLongArray(GRANULARITY); + private volatile int pos = 0; + + private RealTimeCounter() { + PositionUpdater positionUpdater = new PositionUpdater(this); + positionUpdater.start(); + } + + private static volatile RealTimeCounter INSTANCE; + + public static RealTimeCounter getInstance() { + if (INSTANCE == null) { + synchronized (RealTimeCounter.class) { + if (INSTANCE == null) { + INSTANCE = new RealTimeCounter(); + } + } + } + return INSTANCE; + } + + public long getTotalEvents() { + int total = 0; + for (int i = 0; i < GRANULARITY; i++) { + total += counter.get(i); + } + return total; + } + + public void addEvent() { + counter.getAndIncrement(pos); + } + + void incrementPosition() { + //first reset the value to 0 at next counter location. + counter.set((pos + 1) % GRANULARITY, 0); + pos = (pos + 1) % GRANULARITY; + } + + public static void main(String args[]) { + ExecutorService executor = Executors.newFixedThreadPool(10); + RealTimeCounter realTimeCounter = new RealTimeCounter(); + final Random random = new Random(); + final int TOTAL_EVENTS = 10000; + CountDownLatch countDownLatch = new CountDownLatch(TOTAL_EVENTS); + for (int i = 0; i < TOTAL_EVENTS; i++) { + executor.execute(() -> { + realTimeCounter.addEvent(); + try { + Thread.sleep(random.nextInt(10)); + } catch (Exception e) { + e.printStackTrace(); + } + countDownLatch.countDown(); + } + ); + } + try { + countDownLatch.await(); + } catch (Exception e) { + e.printStackTrace(); + } + System.out.println(realTimeCounter.getTotalEvents()); + executor.shutdownNow(); + } +} + +class PositionUpdater extends TimerTask { + + private final RealTimeCounter realTimeCounter; + private final Timer timer = new Timer(true); + private static final int DELAY = 1000; + + PositionUpdater(RealTimeCounter realTimeCounter) { + this.realTimeCounter = realTimeCounter; + } + + public void start() { + timer.schedule(this, DELAY); + } + + @Override + public void run() { + realTimeCounter.incrementPosition(); + } +} \ No newline at end of file diff --git a/src/main/java/multithreading/practice/WebCrawler.java b/src/main/java/multithreading/practice/WebCrawler.java new file mode 100644 index 0000000..8056693 --- /dev/null +++ b/src/main/java/multithreading/practice/WebCrawler.java @@ -0,0 +1,107 @@ +package multithreading.practice; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class WebCrawler { + + Set visited = ConcurrentHashMap.newKeySet(); + Set result = ConcurrentHashMap.newKeySet(); + + public List crawl(String startUrl, HtmlParser htmlParser) { + + visited.add(startUrl); + result.add(startUrl); + List threads = new ArrayList<>(); + + for (String nextUrls : htmlParser.getUrls(startUrl)) { + + if (isUnderSameHost(startUrl, nextUrls) && visited.add(nextUrls)) { + Thread t = new Thread(() -> crawl(nextUrls, htmlParser)); + threads.add(t); + } + } + + for (Thread t : threads) { + t.start(); + } + + for (Thread t : threads) { + try { + t.join(); + } catch (Exception e) { + e.printStackTrace(); + } + } + return new ArrayList<>(result); + } + + private boolean isUnderSameHost(String u1, String u2) { + String[] arr1 = u1.split("/"); + String d1 = arr1[0] + "//" + arr1[2]; + return u2.startsWith(d1); + } + + interface HtmlParser { + List getUrls(String url); + } + + + + public List crawlUsingStreams(String startUrl, HtmlParser htmlParser) { + String hostname = getHostname(startUrl); + + // We don't want to re-crawl pages, so we're going to use a Set that can be modified concurrently + // The following link describes how it is implemented, if you're interested + // https://github.com/frohoff/jdk8u-jdk/blob/master/src/share/classes/java/util/concurrent/ConcurrentHashMap.java#L271 + Set visited = ConcurrentHashMap.newKeySet(); + visited.add(startUrl); + + // This is similar to map-reduce, but instead of reducing to a single value we collect the values + return crawl(startUrl, htmlParser, hostname, visited) + .collect(Collectors.toList()); + } + + private Stream crawl(String startUrl, HtmlParser htmlParser, String hostname, Set visited) { + // htmlParser#getUrls returns a List + Stream stream = htmlParser.getUrls(startUrl) + // We process each url in parallel. The number of threads (parallelism) is decided by the JDK. + .parallelStream() + // We filter out external urls, meaning we don't continue processing them and they're not a part of the result + // This method (isSameHostname) is thread-safe + .filter(url -> isSameHostname(url, hostname)) + // visited is the concurrent set. The add method returns false if the url is already in the set. In that case we ignore the url + // A Set is normally not thread-safe but the one we're using is. + .filter(visited::add) + // If the url passed both filters above, we call crawl on it (recursively). A url generates a Stream + // If we were to use .map we would get something like a List> + // but we need to return a single Stream so we can chain the calls. flatMap concat the multiple streams into a single one + .flatMap(url -> crawl(url, htmlParser, hostname, visited)); + + // We want to return the original crawled url as well as the url's we found by crawling it + // Since startUrl is not part of the stream, we need to add it + // + // Think about what happens to the original url in the flatMap phase: it gets replaced by the Stream of the crawl method + // To keep the startUrl we concat it to the stream. + return Stream.concat(Stream.of(startUrl), stream); + } + + /** Returns the url without the path. (method name wasn't the best) */ + private String getHostname(String url) { + // Start the search after the protocol (http:// is always in the url) + int idx = url.indexOf('/', 7); + // Return the url without the path + return (idx != -1) ? url.substring(0, idx) : url; + } + + private boolean isSameHostname(String url, String hostname) { + if (!url.startsWith(hostname)) { + return false; + } + return url.length() == hostname.length() || url.charAt(hostname.length()) == '/'; + } +} diff --git a/src/main/java/multithreading/practice/ZeroEvenOdd.java b/src/main/java/multithreading/practice/ZeroEvenOdd.java new file mode 100644 index 0000000..fbb98cc --- /dev/null +++ b/src/main/java/multithreading/practice/ZeroEvenOdd.java @@ -0,0 +1,48 @@ +package multithreading.practice; + +import java.util.concurrent.Semaphore; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.IntConsumer; + +class ZeroEvenOdd { + private int n; + + Semaphore zero= new Semaphore(1); + Semaphore one= new Semaphore(0); + Semaphore two= new Semaphore(0); + + AtomicInteger value= new AtomicInteger(1); + + public ZeroEvenOdd(int n) { + this.n = n; + } + + // printNumber.accept(x) outputs "x", where x is an integer. + public void zero(IntConsumer printNumber) throws InterruptedException { + + for(int i=1;i<=n;i++){ + zero.acquire(); + printNumber.accept(0); + if(i%2==0) two.release(); + if(i%2!=0) one.release(); + } + + } + + public void even(IntConsumer printNumber) throws InterruptedException { + + for(int i=2;i<=n;i=i+2){ + two.acquire(); + printNumber.accept(i); + zero.release(); + } + } + + public void odd(IntConsumer printNumber) throws InterruptedException { + for(int i=1;i<=n;i=i+2){ + one.acquire(); + printNumber.accept(i); + zero.release(); + } + } +} \ No newline at end of file diff --git a/src/main/java/multithreading/producerconsumer/Main.java b/src/main/java/multithreading/producerconsumer/Main.java new file mode 100644 index 0000000..9f3077e --- /dev/null +++ b/src/main/java/multithreading/producerconsumer/Main.java @@ -0,0 +1,26 @@ +package multithreading.producerconsumer; + +import java.util.Optional; + +public class Main { + public static void main(String[] args) { + new ProducerConsumer(/* numProducerThreads = */ 1, /* numConsumerThreads = */ 4, + /* queueCapacity = */ 10) { + @Override + public void producer() { + for (int i = 0; i < 100; i++) { + System.out.println("Producing " + i); + produce(i); + } + } + + @Override + public void consumer() { + for (Optional opt; (opt = consume()).isPresent(); ) { + int i = opt.get(); + System.out.println("Got " + i); + } + } + }; + } +} diff --git a/src/main/java/multithreading/producerconsumer/ProducerConsumer.java b/src/main/java/multithreading/producerconsumer/ProducerConsumer.java new file mode 100644 index 0000000..4b504e3 --- /dev/null +++ b/src/main/java/multithreading/producerconsumer/ProducerConsumer.java @@ -0,0 +1,97 @@ +package multithreading.producerconsumer; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; + +public abstract class ProducerConsumer { + + private final BlockingQueue> queue; + + public ProducerConsumer(int numProducerThreads, int numConsumerThreads, int queueCapacity) { + if (numProducerThreads < 1 || numConsumerThreads < 1 || queueCapacity < 1) { + throw new IllegalArgumentException(); + } + queue = new ArrayBlockingQueue<>(queueCapacity); + final ExecutorService executor = Executors.newFixedThreadPool(numProducerThreads + numConsumerThreads); + try { + // Start producer threads + final List> producerFutures = new ArrayList<>(); + final AtomicInteger numLiveProducers = new AtomicInteger(); + for (int i = 0; i < numProducerThreads; i++) { + producerFutures.add(executor.submit(() -> { + numLiveProducers.incrementAndGet(); + // Run producer + producer(); + // When last producer finishes, deliver poison pills to consumers + if (numLiveProducers.decrementAndGet() == 0) { + for (int j = 0; j < numConsumerThreads; j++) { + queue.put(Optional.empty()); + } + } + return null; + })); + } + // Start consumer threads + final List> consumerFutures = new ArrayList<>(); + for (int i = 0; i < numConsumerThreads; i++) { + consumerFutures.add(executor.submit(() -> { + // Run Consumer + consumer(); + return null; + })); + } + // Wait for all producers to complete + completionBarrier(producerFutures, false); + // Shut down any consumers that are still running after producers complete + completionBarrier(consumerFutures, false); + } finally { + executor.shutdownNow(); + } + } + + private static void completionBarrier(List> futures, boolean cancel) { + for (Future future : futures) { + try { + if (cancel) { + future.cancel(true); + } + future.get(); + } catch (CancellationException | InterruptedException e) { + // Ignore + } catch (ExecutionException e) { + throw new RuntimeException(e); + } + } + } + + protected void produce(E val) { + try { + queue.put(Optional.of(val)); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + protected Optional consume() { + try { + return queue.take(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + + /** + * Producer loop. Call {@link #produce(E)} for each element. + */ + public abstract void producer(); + + /** + * Consumer thread. Call {@link #consume()} to get each successive element, + * until an empty {@link Optional} is returned. + */ + public abstract void consumer(); +} + diff --git a/src/main/java/multithreading/thread/Bank.java b/src/main/java/multithreading/thread/Bank.java new file mode 100644 index 0000000..846f8f4 --- /dev/null +++ b/src/main/java/multithreading/thread/Bank.java @@ -0,0 +1,74 @@ +package multithreading.thread; + +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +public class Bank { + private static int bankMoney = 0; + private volatile int account; + private Lock lock; + private Condition condition; + // write your code + + public Bank(int account) { + this.account = account; + // write your code + + lock = new ReentrantLock(); + condition = lock.newCondition(); + } + + public void saveMoney(int amount) throws Exception { + + try { + lock.lock(); + account = saveOperation(account, amount); + if (account >= amount) { + condition.signalAll(); + } + + } finally { + lock.unlock(); + } + + } + + public void withdrawMoney(int amount) throws Exception { + // write your code + + try { + lock.lock(); + while (amount > account) { + condition.await(); + } + account = withdrawOperation(account, amount); + + } finally { + lock.unlock(); + } + } + + public int checkAccount() { + return account; + } + + public static int saveOperation(int account, int amount) throws Exception { + if (bankMoney != account) { + throw new Exception("Don't cheat!\nYour money is " + account + ". The real money is " + bankMoney + "."); + } + bankMoney += amount; + return bankMoney; + } + + public static int withdrawOperation(int account, int amount) throws Exception { + if (bankMoney != account) { + throw new Exception("Don't cheat!\nYour money is " + account + ". The real money is " + bankMoney + "."); + } + if (bankMoney < amount) { + throw new Exception("Money" + bankMoney + " in bank is lowwer than what you want to withdraw(" + account + ")."); + } + bankMoney -= amount; + return bankMoney; + } +} \ No newline at end of file diff --git a/src/main/java/multithreading/thread/BasicMultiThreading.java b/src/main/java/multithreading/thread/BasicMultiThreading.java new file mode 100644 index 0000000..3fb25fc --- /dev/null +++ b/src/main/java/multithreading/thread/BasicMultiThreading.java @@ -0,0 +1,28 @@ +package multithreading.thread; + +public class BasicMultiThreading extends Thread { + + public static void main(String[] args) { + BasicMultiThreading thread = new BasicMultiThreading(); + BasicMultiThreading thread2 = new BasicMultiThreading(); + thread.start(); + thread2.start(); + + thread.run(); + thread2.run(); + + for (int i = 0; i < 5; i++) { + System.out.println("Thread is running on track :" + i + "-->" + thread.getName() + " " + thread.getState()); + System.out.println("Thread 2 is running : " + thread2.getName() + " " + thread2.getState()); + try { + thread.sleep(5000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + + public void printThread() { + + } +} diff --git a/src/main/java/multithreading/thread/DeadLock.java b/src/main/java/multithreading/thread/DeadLock.java new file mode 100644 index 0000000..ed3658d --- /dev/null +++ b/src/main/java/multithreading/thread/DeadLock.java @@ -0,0 +1,44 @@ +package multithreading.thread; + +public class DeadLock { + public static void main(String[] args) { + final String resource1 = "ratan jaiswal"; + final String resource2 = "vimal jaiswal"; + // t1 tries to lock resource1 then resource2 + Thread t1 = new Thread() { + public void run() { + synchronized (resource1) { + System.out.println("Thread 1: locked resource 1"); + + try { + Thread.sleep(100); + } catch (Exception e) { + } + // resource1.notify(); + + synchronized (resource2) { + System.out.println("Thread 1: locked resource 2"); + } + + } + } + }; + + // t2 tries to lock resource2 then resource1 + Thread t2 = new Thread() { + public void run() { + synchronized (resource2) { + System.out.println("Thread 2: locked resource 2"); + //resource2.notify(); + synchronized (resource1) { + System.out.println("Thread 2: locked resource 1"); + } + } + } + }; + + t1.start(); + t2.start(); + } + +} diff --git a/src/main/java/multithreading/thread/PrintEvenOddTester.java b/src/main/java/multithreading/thread/PrintEvenOddTester.java new file mode 100644 index 0000000..e5f4009 --- /dev/null +++ b/src/main/java/multithreading/thread/PrintEvenOddTester.java @@ -0,0 +1,68 @@ +package multithreading.thread; + +public class PrintEvenOddTester { + + public static void main(String... args) { + Printer print = new Printer(); + Thread t1 = new Thread(new TaskEvenOdd(print, 10, false)); + Thread t2 = new Thread(new TaskEvenOdd(print, 10, true)); + t1.start(); + t2.start(); + } +} + +class TaskEvenOdd implements Runnable { + + private int max; + private Printer print; + private boolean isEvenNumber; + + TaskEvenOdd(Printer print, int max, boolean isEvenNumber) { + this.print = print; + this.max = max; + this.isEvenNumber = isEvenNumber; + } + + @Override + public void run() { + int number = isEvenNumber == true ? 2 : 1; + while (number <= max) { + if (isEvenNumber) { + print.printEven(number); + } else { + print.printOdd(number); + } + number += 2; + } + } +} + +class Printer { + boolean isOdd = false; + + synchronized void printEven(int number) { + while (isOdd == false) { + try { + wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + System.out.println("Even:" + number + "::" + Thread.currentThread().getName()); + isOdd = false; + notifyAll(); + } + + synchronized void printOdd(int number) { + while (isOdd == true) { + try { + wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + System.out.println("Odd:" + number + "::" + Thread.currentThread().getName()); + isOdd = true; + notifyAll(); + } +} \ No newline at end of file diff --git a/src/main/java/multithreading/thread/PrintOddEvenByTwoThreads.java b/src/main/java/multithreading/thread/PrintOddEvenByTwoThreads.java new file mode 100644 index 0000000..9ba68aa --- /dev/null +++ b/src/main/java/multithreading/thread/PrintOddEvenByTwoThreads.java @@ -0,0 +1,60 @@ +package multithreading.thread; +public class PrintOddEvenByTwoThreads { + static int number = 1; + static Thread odd; + static Thread even; + static int max = 10; + + static class OddThread extends Thread { + @Override + public void run() { + while (number <= max) { + if (number % 2 == 1) { + System.out.println(Thread.currentThread() + "" + number++); + } else { + + synchronized (odd) { + synchronized (even) { + even.notify(); + } + try { + wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } + } + } + + static class EvenThread extends Thread { + @Override + public void run() { + while (number <= max) { + if (number % 2 == 0) { + System.out.println(Thread.currentThread() + "" + number++); + } else { + + synchronized (even) { + synchronized (odd) { + odd.notify(); + } + try { + wait(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + } + } + } + + public static void main(String[] args) throws InterruptedException { + odd = new OddThread(); + even = new EvenThread(); + odd.start(); + even.start(); + } +} \ No newline at end of file diff --git a/src/main/java/multithreading/thread/RaceConditionExample.java b/src/main/java/multithreading/thread/RaceConditionExample.java new file mode 100644 index 0000000..c2445ca --- /dev/null +++ b/src/main/java/multithreading/thread/RaceConditionExample.java @@ -0,0 +1,54 @@ +package multithreading.thread; + +class Counter implements Runnable{ + private int c = 0; + + public void increment() { + try { + Thread.sleep(10); + } catch (InterruptedException e) { + e.printStackTrace(); + } + c++; + } + + public void decrement() { + c--; + } + + public int getValue() { + return c; + } + + @Override + public void run() { + //synchronized(this) { + // incrementing + for(int i=0;i<100;i++) { + this.increment(); +// System.out.println("Value for Thread After increment " +// + Thread.currentThread().getName() + " " + this.getValue()); + //decrementing + this.decrement(); +// System.out.println("Value for Thread at last " + Thread.currentThread().getName() +// + " " + this.getValue()); + //} + } + } +} + +public class RaceConditionExample { + public static void main(String[] args) { + Counter counter = new Counter(); + Thread t1 = new Thread(counter, "Thread-A"); + Thread t2 = new Thread(counter, "Thread-B"); + Thread t3 = new Thread(counter, "Thread-C"); + t1.start(); + t2.start(); + t3.start(); + + + + System.out.println("#### "+counter.getValue()); + } +} \ No newline at end of file diff --git a/src/main/java/multithreading/thread/Tesst.java b/src/main/java/multithreading/thread/Tesst.java new file mode 100644 index 0000000..dcb8ca7 --- /dev/null +++ b/src/main/java/multithreading/thread/Tesst.java @@ -0,0 +1,38 @@ +package multithreading.thread; + +public class Tesst { + + static class ThreadA { + + public static void main(String [] args) { + ThreadB b = new ThreadB(); + b.setName("test1"); + b.start(); + + synchronized(b) { + System.out.println("inside sync"); + try { + b.wait(); + System.out.println("Waiting for b to complete..."); + + System.out.println("after "+" "+b.getState()); + } catch (InterruptedException e) {} + System.out.println("Total is: " + b.total); + } + } + } + + static class ThreadB extends Thread { + int total; + + public void run() { + System.out.println("total"+total); + synchronized(this) { + for(int i = 0; i < 100; i++) { + total += i; + } + notify(); + } + } + } +} diff --git a/src/main/java/multithreading/thread/TestForLocking.java b/src/main/java/multithreading/thread/TestForLocking.java new file mode 100644 index 0000000..a293073 --- /dev/null +++ b/src/main/java/multithreading/thread/TestForLocking.java @@ -0,0 +1,51 @@ +package multithreading.thread; + +import java.util.Date; + +public class TestForLocking { + public static void main(String[] args) throws Exception { + testCuncurrency(); + } + + private static void testCuncurrency() throws InterruptedException { + Object lock = new Object(); + Thread t1 = new Thread(new WaitTester(lock)); + Thread t2 = new Thread(new WaitTester(lock)); + t1.start(); + t2.start(); + Thread.sleep(15 * 1000); + synchronized (lock) { + System.out.println("Time: " + new Date().toString()+ ";" + "Notifying all"); + lock.notifyAll(); + } + } + + private static class WaitTester implements Runnable { + private Object lock; + public WaitTester(Object lock) { + this.lock = lock; + } + + @Override + public void run() { + try { + synchronized (lock) { + System.out.println(getTimeAndThreadName() + ":only one thread can be in synchronized block"); + Thread.sleep(5 * 1000); + + System.out.println(getTimeAndThreadName() + ":thread goes into waiting state and releases the lock"); + lock.wait(); + + System.out.println(getTimeAndThreadName() + ":thread is awake and have reacquired the lock"); + + System.out.println(getTimeAndThreadName() + ":syncronized block have finished"); + } + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + private static String getTimeAndThreadName() { + return "Time: " + new Date().toString() + ";" + Thread.currentThread().getName(); + } +} \ No newline at end of file diff --git a/src/main/java/multithreading/thread/ThreadJoinExample.java b/src/main/java/multithreading/thread/ThreadJoinExample.java new file mode 100644 index 0000000..b25a612 --- /dev/null +++ b/src/main/java/multithreading/thread/ThreadJoinExample.java @@ -0,0 +1,57 @@ +package multithreading.thread; + +public class ThreadJoinExample { + + public static void main(String[] args) { + Thread t1 = new Thread(new MyRunnable(), "t1"); + Thread t2 = new Thread(new MyRunnable(), "t2"); + Thread t3 = new Thread(new MyRunnable(), "t3"); + + t1.start(); + + //start second thread after waiting for 2 seconds or if it's dead + try { + t1.join(2000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + t2.start(); + + //start third thread only when first thread is dead + try { + t1.join(); + } catch (InterruptedException e) { + e.printStackTrace(); + } + + t3.start(); + + //let all threads finish execution before finishing main thread +// try { +// t1.join(); +// t2.join(); +// t3.join(); +// } catch (InterruptedException e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// } + + System.out.println("All threads are dead, exiting main thread"); + } + +} + +class MyRunnable implements Runnable { + + @Override + public void run() { + System.out.println("Thread started: "+Thread.currentThread().getName()); + try { + Thread.sleep(4000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println("Thread ended: "+Thread.currentThread().getName()); + } +} \ No newline at end of file diff --git a/src/main/java/multithreading/thread/ThreadRunnable.java b/src/main/java/multithreading/thread/ThreadRunnable.java new file mode 100644 index 0000000..5a3b407 --- /dev/null +++ b/src/main/java/multithreading/thread/ThreadRunnable.java @@ -0,0 +1,32 @@ +package multithreading.thread; + +public class ThreadRunnable implements Runnable { + + public static void main(String[] args) throws InterruptedException { + ThreadRunnable runnableThread = new ThreadRunnable(); + Thread t1 = new Thread(runnableThread); + t1.setName("t1"); + Thread t2 = new Thread(runnableThread); + t2.setName("t2"); + + t1.setPriority(Thread.MIN_PRIORITY); + t1.start(); + + t2.setPriority(Thread.MAX_PRIORITY); + t2.start(); + } + + @Override + public void run() { + for (int i = 0; i < 5; i++) { + if (Thread.currentThread().getName().equals("t1")) + System.out.println(Thread.currentThread().getName() + "--" + i); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + } + +} diff --git a/src/main/java/practiceproblems/AdvantageShuffle.java b/src/main/java/practiceproblems/AdvantageShuffle.java new file mode 100644 index 0000000..40ca136 --- /dev/null +++ b/src/main/java/practiceproblems/AdvantageShuffle.java @@ -0,0 +1,59 @@ +package practiceproblems; + +// the advantage of A with respect to B is the number of indices i for which A[i] > B[i]. +/** + * If the smallest card a in A beats the smallest card b in B, we should pair them. + * Otherwise, a is useless for our score, as it can't beat any cards. + * Why should we pair a and b if a > b? + * Because every card in A is larger than b, any card we place in front of b will score a point. + * We might as well use the weakest card to pair with b as it makes the rest of the cards in A strictly larger, + * and thus have more potential to score points. + */ + +import java.util.Arrays; +import java.util.PriorityQueue; + +// Return any permutation of A that maximizes its advantage with respect to B. +//Input: A = [12,24,8,32], B = [13,25,32,11] +//Output: [24,32,8,12] +// Input: A = [2,7,11,15], B = [1,10,4,11] +// Output: [2,11,7,15] +public class AdvantageShuffle { + public static int[] advantageCount(int[] A, int[] B) { + Arrays.sort(A); + + var pq = new PriorityQueue((a, b) -> Integer.compare(b[0], a[0])); + for (int i = 0; i < B.length; i++) { + pq.offer(new Integer[]{B[i], i}); // add elements of B along with its index to max queue + } + int[] result = new int[A.length]; // new placeholder for result + int lo = 0; + int hi = A.length - 1; // start and end index + //B is transformed to [32,25,13,11] + //A is transformed to [8,12,24,32] + while (!pq.isEmpty()) { + var temp = pq.poll(); + int index = temp[1]; + int val = temp[0]; + /** + * Two Rules: + * 1) we should satisfy the biggest element first, because that's the hardest to satisfy + * 2) If bigger element is not satisfiable we should put min element there and move on + */ + if (A[hi] > val) { // if polled element is lesser thar A[hi], put A[hi] at index of + // queued elements index, means, equal to B's current index we are putting + // a value greater that B's in result arrays + result[index] = A[hi--]; + } else { + // else we put the lowest value at this position because no matter we try + // we won't be getting a value larger than 'val' + result[index] = A[lo++]; + } + } + return result; + } + + public static void main(String[] args) { + advantageCount(new int[]{12,24,8,32}, new int[]{13,25,32,11}); + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/AircraftMovieDuration.java b/src/main/java/practiceproblems/AircraftMovieDuration.java new file mode 100644 index 0000000..bdf7ce8 --- /dev/null +++ b/src/main/java/practiceproblems/AircraftMovieDuration.java @@ -0,0 +1,62 @@ +package practiceproblems; + +import java.util.*; + +/** + * You are on a flight and wanna watch two movies during this flight. + * You are given List movieDurations which includes all the movie durations. + * You are also given the duration of the flight which is d in minutes. + * Now, you need to pick two movies and the total duration of the two movies is less than or equal to (d - 30min). + *

+ * Find the pair of movies with the longest total duration and return they indexes. If multiple found, return the pair with the longest movie. + */ +public class AircraftMovieDuration { + + public static void main(String[] args) { + int[] movie_duration1 = {90, 85, 75, 60, 120, 150, 125}; + int d1 = 250; + int[] movie_duration2 = {90, 85, 75, 60, 155, 150, 125}; + int d2 = 250; + int[] movie_duration3 = {90, 85, 75, 60, 120, 110, 110, 150, 125}; + int d3 = 250; + System.out.println(Arrays.toString(get2SumClosest(movie_duration1, d1 - 30))); + System.out.println(Arrays.toString(get2SumClosest(movie_duration2, d2 - 30))); + System.out.println(Arrays.toString(get2SumClosest(movie_duration3, d3 - 30))); + } + + private static int[] get2SumClosest(int[] movie_duration, int d) { + // we need to preserve the input order before sorting + //Noticed simply using hashmap does not work if you need to return the index of same value, + // we need to track all index with same value. + Map> map = new HashMap<>(); + for (int i = 0; i < movie_duration.length; i++) { + map.putIfAbsent(movie_duration[i], new ArrayList<>()); + map.get(movie_duration[i]).add(i); + } + Arrays.sort(movie_duration); + int l = 0, r = movie_duration.length - 1; + int max = 0; + int[] res = new int[]{-1, -1}; + while (l < r) { + int sum = movie_duration[l] + movie_duration[r]; + boolean isSumGreater = sum > max; + boolean isSumEqualAndDurationGreater = sum == max && movie_duration[r] > res[1]; + boolean isSumWithinDuration = sum <= d; + + if ((isSumGreater || isSumEqualAndDurationGreater) && isSumWithinDuration) { + max = sum; + res[0] = movie_duration[l]; + res[1] = movie_duration[r]; + } + + int direction = sum > d ? -1 : 1; + if (direction == -1) r += direction; + else l += direction; +} + +boolean isSameList = map.get(res[0]) == map.get(res[1]); +res[0] = map.get(res[0]).get(0); +res[1] = map.get(res[1]).get(isSameList ? 1 : 0); + return res; + } +} diff --git a/src/main/java/practiceproblems/AircraftOptimization.java b/src/main/java/practiceproblems/AircraftOptimization.java new file mode 100644 index 0000000..03e6987 --- /dev/null +++ b/src/main/java/practiceproblems/AircraftOptimization.java @@ -0,0 +1,69 @@ +package practiceproblems; + +import java.util.*; + +/** + * https://leetcode.com/discuss/interview-question/373202 + * Given 2 lists a and b. Each element is a pair of integers where the first integer represents the unique id and the second integer represents a value. + * Your task is to find an element from a and an element form b such that the sum of their values is less or equal to target and as close to target as possible. + * Return a list of ids of selected elements. If no pair is possible, return an empty list. + * + * a = [[1, 2], [2, 4], [3, 6]] + b = [[1, 2]] + target = 7 + + Output: [[2, 1]] + + Explanation: + There are only three combinations [1, 1], [2, 1], and [3, 1], which have a total sum of 4, 6 and 8, respectively. + Since 6 is the largest sum that does not exceed 7, [2, 1] is the optimal pair. + */ +public class AircraftOptimization { + + public List> calculateOptimalRoute(final List> forwardList, + final List> returnList, int capacity) { + + forwardList.sort(Comparator.comparingInt(o->o.get(1))); + returnList.sort(Comparator.comparingInt(o->o.get(1))); + + int max = 0; + int i = 0; + int j = returnList.size() - 1; + + List> result = new LinkedList<>(); + while (i < forwardList.size() && j >= 0) { + int currentSum = forwardList.get(i).get(1) + returnList.get(j).get(1); + + if (currentSum > max && currentSum <= capacity) { + max = currentSum; + // got a new max so Initializing new list + result = new LinkedList<>(); + result.add(new ArrayList<>(Arrays.asList(forwardList.get(i).get(0), returnList.get(j).get(0)))); + i++; + } else if (currentSum == max) { + // no need to reset result list + result.add(new ArrayList<>(Arrays.asList(forwardList.get(i).get(0), returnList.get(j).get(0)))); + i++; + } else { + j--; + } + } + return result; + } + + public static void main(String[] args) { + AircraftOptimization aircraft = new AircraftOptimization(); + List> returnList = new ArrayList<>(); + returnList.add(new ArrayList<>(Arrays.asList(1, 2000))); + returnList.add(new ArrayList<>(Arrays.asList(2, 3000))); + returnList.add(new ArrayList<>(Arrays.asList(3, 4000))); + returnList.add(new ArrayList<>(Arrays.asList(4, 5000))); + List> forwardList = new ArrayList<>(); + forwardList.add(new ArrayList<>(Arrays.asList(1, 3000))); + forwardList.add(new ArrayList<>(Arrays.asList(2, 5000))); + forwardList.add(new ArrayList<>(Arrays.asList(3, 7000))); + forwardList.add(new ArrayList<>(Arrays.asList(4, 10000))); + List> calculateOptimalRoute = aircraft.calculateOptimalRoute(forwardList, returnList, 10000); + System.out.println(calculateOptimalRoute); + } +} diff --git a/src/main/java/practiceproblems/AlternateOddAndEvenNumbers.java b/src/main/java/practiceproblems/AlternateOddAndEvenNumbers.java new file mode 100644 index 0000000..13ab9bc --- /dev/null +++ b/src/main/java/practiceproblems/AlternateOddAndEvenNumbers.java @@ -0,0 +1,49 @@ +package practiceproblems; + +import java.util.Arrays; + +/** + * https://www.geeksforgeeks.org/rearrange-positive-and-negative-numbers-publish/ + * + * An array contains both positive and negative numbers in random order. + * Rearrange the array elements so that positive and negative numbers are placed alternatively. + * If there are more positive numbers they appear at the end of the array. + * If there are more negative numbers, they too appear in the end of the array. + * [-1, 2, -3, 4, 5, 6, -7, 8, 9] + * output [9, -7, 8, -3, 5, -1, 2, 4, 6] or[4, -3, 5, -1, 6, -7, 2, 8, 9] + */ +class AlternateOddAndEvenNumbers { + + static void rearrange(int[] arr, int n) { + //-1, 2, -3, 4, 5, 6, -7, 8, 9 + int i = 0, temp; + for (int j = 0; j < n; j++) { + if (arr[j] < 0) { + temp = arr[i]; + arr[i] = arr[j]; + arr[j] = temp; + i++; + } + } + // we have segregated positive and negative elements + System.out.println(Arrays.toString(arr) + " :i - " + i); + // now the 'pos' indicates start of positive integer, 'negPos' starts from 0 + int pos = i, negPos = 0; + + + while (pos < n && negPos < pos && arr[negPos] < 0) { + temp = arr[negPos]; + arr[negPos] = arr[pos]; + arr[pos] = temp; + pos++; + negPos += 2; // need to skip next element as output should be alternative + } + } + + public static void main(String[] args) { + int arr[] = {-1, 2, -3, 4, 5, 6, -7, 8, 9}; + int n = arr.length; + rearrange(arr, n); + System.out.println("Array after rearranging: " + Arrays.toString(arr)); + } +} diff --git a/src/main/java/practiceproblems/AngleOfClock.java b/src/main/java/practiceproblems/AngleOfClock.java new file mode 100644 index 0000000..d0dc121 --- /dev/null +++ b/src/main/java/practiceproblems/AngleOfClock.java @@ -0,0 +1,15 @@ +package practiceproblems; + +public class AngleOfClock { + public double angleClock(int hours, int minutes) { + // every hour is 30(deg) (360 (deg)/12) because 12 hrs in clock + // every min is 6(deg) (360(deg)/60) because 60 mins per hr + // we take mod of 12 because 12th hr needs to be 0* + double hourHand = (hours % 12 + (double) minutes / 60) * 30; + double minHand = minutes * 6; + double absAngle = Math.abs(hourHand - minHand); + if (absAngle > 180) absAngle = 360 - absAngle; // this is because when time is 0.02 the angel will be 358 + + return absAngle; + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/ArrangeInQueue.java b/src/main/java/practiceproblems/ArrangeInQueue.java new file mode 100644 index 0000000..1257d09 --- /dev/null +++ b/src/main/java/practiceproblems/ArrangeInQueue.java @@ -0,0 +1,39 @@ +package practiceproblems; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + + +//https://leetcode.com/problems/queue-reconstruction-by-height/description/ +public class ArrangeInQueue { + + // 1. Sort people by their height, shortest to tallest +// 2. Iterate and put each person to the correct position +// 2.a When placing the shortest person, all person to his left will be taller or equal height, since you are iterating in height sorted array, so put it at a index equal to its k value +// 2.b When placing the next shortest person, find a position, where count of positions to the left unoccupied plus the ones where same height person is placed, is equal to its k value +// 2.c Keep repeating + public static int[][] reconstructQueue(int[][] people) { + List result = new ArrayList<>(); + Arrays.sort(people, (a, b) -> { + if (a[0] == b[0]) return a[1] - b[1]; + else return b[0] - a[0]; + }); + //Here's a potential algorithm: + //a. Sort the people array in descending order of height. If heights are equal, sort by ascending order of 'k'. + //b. Create an empty result array to represent the queue. + //c. Iterate through the sorted people array: + // + //For each person, insert them into the result array at index 'k'. + //This works because we're inserting taller people first, so the 'k' value directly corresponds to their position. + for (int[] x : people) { + result.add(x[1], x); + System.out.println(Arrays.deepToString(result.toArray(new int[people.length][2]))); + } + return result.toArray(new int[people.length][2]); + } + + public static void main(String[] args) { + System.out.println(Arrays.deepToString(reconstructQueue(new int[][]{{7, 0}, {4, 4}, {7, 1}, {5, 0}, {6, 1}, {5, 2}}))); + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/BackspaceCompare.java b/src/main/java/practiceproblems/BackspaceCompare.java new file mode 100644 index 0000000..9d9b108 --- /dev/null +++ b/src/main/java/practiceproblems/BackspaceCompare.java @@ -0,0 +1,33 @@ +package practiceproblems; + +/** + * https://leetcode.com/problems/backspace-string-compare/ + */ +class BackspaceCompare { + + public static boolean backspaceCompare(String S, String T) { + int count1 = 0; // stores the number of # in string 1 + int count2 = 0; // stores the number of # in string 2 + for (int p1 = S.length() - 1, p2 = T.length() - 1; p1 >= 0 || p2 >= 0; p1--, p2--) { + + while (p1 >= 0 && (count1 != 0 || S.charAt(p1) == '#')) { + if (S.charAt(p1) == '#') count1++; + else count1--; + p1--; + } + while (p2 >= 0 && (count2 != 0 || T.charAt(p2) == '#')) { + if (T.charAt(p2) == '#') count2++; + else count2--; + p2--; + } + if (p1 < 0 && p2 < 0) return true; + if (p1 < 0 || p2 < 0) return false; + if (S.charAt(p1) != T.charAt(p2)) return false; + } + return true; + } + + public static void main(String[] args) { + System.out.println(backspaceCompare("a##c", "#a#c")); + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/BinaryTreeCousins.java b/src/main/java/practiceproblems/BinaryTreeCousins.java new file mode 100644 index 0000000..461ce74 --- /dev/null +++ b/src/main/java/practiceproblems/BinaryTreeCousins.java @@ -0,0 +1,45 @@ +package practiceproblems; + +import trees.TreeNode; + +import java.util.ArrayDeque; +import java.util.Deque; +/** + * https://leetcode.com/problems/cousins-in-binary-tree + */ +public class BinaryTreeCousins { + + public boolean isCousins(TreeNode root, int x, int y) { + + Deque queue = new ArrayDeque<>(); + queue.offer(root); + + while (!queue.isEmpty()) { + int n = queue.size(); + boolean xFound = false; + boolean yFound = false; + + for (int i = 0; i < n; i++) { + TreeNode temp = queue.poll(); + + if (temp.left != null && temp.right != null) { + if (temp.left.val == x && temp.right.val == y) return false; + if (temp.left.val == y && temp.right.val == x) return false; + } + + if (temp.val == x) xFound = true; + if (temp.val == y) yFound = true; + + if (temp.left != null) + queue.addLast(temp.left); + if (temp.right != null) + queue.addLast(temp.right); + } + + if (xFound && yFound) return true; + if (yFound || xFound) return false; + } + + return true; + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/BuildArray.java b/src/main/java/practiceproblems/BuildArray.java new file mode 100644 index 0000000..766b062 --- /dev/null +++ b/src/main/java/practiceproblems/BuildArray.java @@ -0,0 +1,64 @@ +package practiceproblems; + +import java.util.Arrays; + +/** + * https://leetcode.com/problems/build-array-from-permutation/ + * + * tricky + */ +public class BuildArray { + + public static int[] buildArray(int[] nums) { + int n = nums.length; + int i = 0; + /** + a = r +bq Euclid's Division algorithm + let r = nums[i] + replace nums[i] with a number that: + + when you mod by q you get nums[i] + when you divide by q you get nums[nums[i]] + From a = r + bq + + we can observe that + + a % q = r + a / q = b + a % q = r + + Since a = r + bq, we can write: + (r + bq) % q = r % q + (bq % q) = r % q + 0 = r + This is because any multiple of q will give a remainder of 0 when divided by q. + + a / q = b + Again, using a = r + bq: + (r + bq) / q = r/q + b + Since r < q (as r is a remainder), r/q will be 0 in integer division. + So, (r + bq) / q = 0 + b = b + + nums[i] is stored as r (the remainder) + nums[nums[i]] is stored as b (the quotient) + n is stored as q + */ + + while (i < nums.length) { + int temp = nums[i] + n * (nums[nums[i]] % n); + nums[i] = temp; + i++; + } + + System.out.println(Arrays.toString(nums)); + + i = 0; + while (i < nums.length) { + nums[i] /= n; + i++; + } + return nums; + } + + public static void main(String[] args) { + System.out.println(Arrays.toString(buildArray(new int[]{5, 0, 1, 2, 3, 4}))); + } +} diff --git a/src/main/java/practiceproblems/CanPlaceFlower.java b/src/main/java/practiceproblems/CanPlaceFlower.java new file mode 100644 index 0000000..16ec9f7 --- /dev/null +++ b/src/main/java/practiceproblems/CanPlaceFlower.java @@ -0,0 +1,60 @@ +package practiceproblems; + +/** + * https://leetcode.com/problems/can-place-flowers/ + */ +public class CanPlaceFlower { + + public boolean canPlaceFlowers(int[] flowerbed, int n) { + + int flowerCounts = 0; + for (int i = 0; i < flowerbed.length && flowerCounts < n; i++) { + if (flowerbed[i] == 0) { + int prev = i > 0 ? flowerbed[i - 1] : 0; + int next = i < flowerbed.length - 1 ? flowerbed[i + 1] : 0; + + if (prev == 0 && next == 0) { + flowerbed[i] = 1; + flowerCounts++; + } + } + } + + + return flowerCounts == n; + } + + /** + * This is the logic the code is using- + * + * If there are "count" zeroes in between two 1s, + * then how many 1s can we place in those zeroes without violating the given condition? + * Answer is "(count-1)/2". + * The only cases this doesn't apply are when there are zeroes(1 or more) + * At the beginning of the array. + * At the end of the array. + * + * For these 2 cases,the number of 1s that we can place is count/2. + * But to generalize the algorithm and to simplify code inside loop, + * count has been initialized to 1 for the first time and result += (count-1)/2 effectively becomes result += count/2 for the case 1. + * For case 2, result is updated outside the loop, again by count/2 times. + * + * Finally, we check if the number of possible 1s that we can place is greater than or equal to n. If so, we return true else false. + * + * smart to set count = 1 to solve the problem that the beginning of flowerbed is 0 + */ + public boolean canPlaceFlowersNonMutating(int[] flowerbed, int n) { + int count = 1; + int result = 0; + for (int j : flowerbed) { + if (j == 0) { + count++; + } else { + result += (count - 1) / 2; + count = 0; + } + } + if(count != 0) result += count/2; + return result>=n; + } +} diff --git a/src/main/java/practiceproblems/Candy.java b/src/main/java/practiceproblems/Candy.java new file mode 100644 index 0000000..c8c04f6 --- /dev/null +++ b/src/main/java/practiceproblems/Candy.java @@ -0,0 +1,58 @@ +package practiceproblems; + +import java.util.Arrays; + +/** + * https://leetcode.com/problems/candy/ + * Alice wants to give at least 1 candy to each child. + * If two children sit next to each other, then + * the one with the higher rating must get more candies than neighbour (left and right). + * Alice wants to minimize the total number of candies she must buy. + *

+ * For example, assume her students' ratings are [4, 6, 4, 5, 6, 2]. + * She gives the students candy in the following minimal amounts: [1, 2, 1, 2, 3, 1]. She must buy a minimum of 10 candies. + */ +class Candy { + + // 2, 4, 2, 6, 1, 7, 8, 3, 2, 1 + // 1, 2, 1, 2, 1, 2, 3, 1, 1, 1 + // 1, 2, 4, 3, 2, 1 + int candy(int[] ratings) { + int size = ratings.length; + if (size <= 1) { + return size; + } + + // ideally we should maintain 2 arrays 1)L->R 2) R->L + // then iterate both and check max b/w 2 items as result for each index + // we optimise it further for extra space and for loop + int[] num = new int[size]; + Arrays.fill(num, 1); + // left to right + for (int i = 1; i < size; i++) { + if (ratings[i] > ratings[i - 1]) { + num[i] = num[i - 1] + 1; + } + } + + // right to left + for (int i = size - 1; i > 0; i--) { + if (ratings[i - 1] > ratings[i]) { + // check curr index + 1 or existing value + num[i - 1] = Math.max(num[i] + 1, num[i - 1]); + } + } + int result = 0; + for (int i = 0; i < size; i++) { + result += num[i]; + } + return result; + } + + public static void main(String[] args) { + Candy candy = new Candy(); + + int[] ratings = {2, 4, 2, 6, 1, 7, 8, 9, 2, 1}; + System.out.println(candy.candy(ratings)); + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/CelebrityProblem.java b/src/main/java/practiceproblems/CelebrityProblem.java new file mode 100644 index 0000000..1236cca --- /dev/null +++ b/src/main/java/practiceproblems/CelebrityProblem.java @@ -0,0 +1,54 @@ +package practiceproblems; + +/** + * tricky + */ +public class CelebrityProblem { + /** + * @param n a party with n people + * @return the celebrity's label or -1 + */ + public int findCelebrity(int n) { + int candidate = 0; + for (int i = 1; i < n; i++) { + if (knows(candidate, i)) { + candidate = i; + } + } + + for (int i = 0; i < n; i++) { + if (i != candidate && (knows(candidate, i) || !knows(i, candidate))) { + return -1; + } + } + + return candidate; + } + + public int findCelebrityGraph(int n) { + int[] inDegree = new int[n]; + int[] outDegree = new int[n]; + + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + if (j == i) continue; + + if (knows(j, i)) { + outDegree[j]++; + inDegree[i]++; + } + } + } + + for (int i = 0; i < n; i++) { + if (inDegree[i] == n - 1 && outDegree[i] == 0) + return i; + } + + return -1; + } + + public boolean knows(int candidate, int i) { + return false; + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/CheckPalindromePermutation.java b/src/main/java/practiceproblems/CheckPalindromePermutation.java new file mode 100644 index 0000000..af22e51 --- /dev/null +++ b/src/main/java/practiceproblems/CheckPalindromePermutation.java @@ -0,0 +1,32 @@ +package practiceproblems; + +import java.util.Arrays; + +/** + * Given a string, determine if a permutation of the string could form a palindrome. + * Input: "code" + * Output: false + * Input: "carerac" + Output: true + */ +public class CheckPalindromePermutation { + // If a string with an even length is a palindrome, every character in the string must always occur an even number of times. + // If the string with an odd length is a palindrome, every character except one of the characters must always occur an even number of times. + // Thus, in case of a palindrome, the number of characters with odd number of occurrences can't exceed 1 + + public boolean canPermutePalindrome(String s) { + int[] cache = new int[26]; + + for(char t: s.toCharArray()){ + if(cache[t-'a']>0){ + cache[t-'a']--; + }else{ + cache[t-'a']++; + } + } + + int result = Arrays.stream(cache).sum(); + + return result<=1; + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/CompareVersions.java b/src/main/java/practiceproblems/CompareVersions.java new file mode 100644 index 0000000..12aa68a --- /dev/null +++ b/src/main/java/practiceproblems/CompareVersions.java @@ -0,0 +1,31 @@ +package practiceproblems; + +/** + * https://leetcode.com/problems/compare-version-numbers/ + */ +public class CompareVersions { + + public static void main(String[] args) { + String str1 = "1.3.4"; + String str2 = "1.3.1.7"; + System.out.println(compareVersion(str1, str2)); + } + + public static int compareVersion(String version1, String version2) { + String[] levels1 = version1.split("\\."); + String[] levels2 = version2.split("\\."); + + int length = Math.max(levels1.length, levels2.length); + for (int i = 0; i < length; i++) { + Integer v1 = i < levels1.length ? Integer.parseInt(levels1[i]) : 0; + Integer v2 = i < levels2.length ? Integer.parseInt(levels2[i]) : 0; + int compare = v1.compareTo(v2); + if (compare != 0) { + return compare; + } + } + + return 0; + } + +} diff --git a/src/main/java/practiceproblems/ComplexNumberMultiply.java b/src/main/java/practiceproblems/ComplexNumberMultiply.java new file mode 100644 index 0000000..e98142c --- /dev/null +++ b/src/main/java/practiceproblems/ComplexNumberMultiply.java @@ -0,0 +1,28 @@ +package practiceproblems; + +/** + * https://leetcode.com/problems/complex-number-multiplication/ + */ +public class ComplexNumberMultiply { + + public String complexNumberMultiply(String num1, String num2) { + String[] first = num1.split("\\+"); + String[] second = num2.split("\\+"); + int aSq = 0; + int bSq = -1; + int ab = 0; + + aSq += Integer.parseInt(first[0]) * Integer.parseInt(second[0]); + // remove i from the string and multiply so substring(0, length - 1) + bSq *= Integer.parseInt(first[1].substring(0, first[1].length() - 1)) * Integer.parseInt(second[1].substring(0, second[1].length() - 1)); + ab += (Integer.parseInt(first[0]) * Integer.parseInt(second[1].substring(0, second[1].length() - 1))) + + (Integer.parseInt(second[0]) * Integer.parseInt(first[1].substring(0, first[1].length() - 1))); + + return (aSq + bSq) + "+" + ab + "i"; + } + + public static void main(String[] args) { + ComplexNumberMultiply complexNumberMultiply = new ComplexNumberMultiply(); + System.out.println(complexNumberMultiply.complexNumberMultiply("1+1i", "1+1i")); + } +} diff --git a/src/main/java/practiceproblems/ContainerWithMostWater.java b/src/main/java/practiceproblems/ContainerWithMostWater.java new file mode 100644 index 0000000..002323c --- /dev/null +++ b/src/main/java/practiceproblems/ContainerWithMostWater.java @@ -0,0 +1,30 @@ +package practiceproblems; + +/** + * https://leetcode.com/problems/container-with-most-water/ + * tricky + */ +public class ContainerWithMostWater { + + public int maxArea(int[] height) { + if (height == null || height.length == 0) return 0; + int i = 0; + int j = height.length - 1; + int result = 0; + + while (i < j) { + + int waterVol = (j - i) * Math.min(height[i], height[j]); // width * height + result = Math.max(waterVol, result); + + if (height[i] > height[j]) { // the reason we are moving lesser side is + // j-i is going to be decreasing, so we need to + // maintain higher side to have max value + j--; + } else { + i++; + } + } + return result; + } +} diff --git a/src/main/java/practiceproblems/ConvertXToY.java b/src/main/java/practiceproblems/ConvertXToY.java new file mode 100644 index 0000000..e9c3928 --- /dev/null +++ b/src/main/java/practiceproblems/ConvertXToY.java @@ -0,0 +1,70 @@ +package practiceproblems; +// Java program to find minimum +// number of steps needed to +// convert a number x into y +// with two operations allowed : +// (1) multiplication with 2 +// (2) subtraction with 1. + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Set; + +/** + * https://www.geeksforgeeks.org/minimum-number-operation-required-convert-number-x-y/ + */ + +public class ConvertXToY { + + private static int minOperations(int src, int target) { + + Set visited = new HashSet<>(); + LinkedList queue = new LinkedList<>(); + + Steps node = new Steps(src, 0); + + queue.offer(node); + visited.add(node); + + while (!queue.isEmpty()) { + Steps temp = queue.poll(); + visited.add(temp); + + if (temp.val == target) { + return temp.steps; + } + + int mul = temp.val * 2; + int sub = temp.val - 1; + + // given constraints + if (mul > 0 && mul < 1000) { + Steps nodeMul = new Steps(mul, temp.steps + 1); + queue.offer(nodeMul); + } + if (sub > 0 && sub < 1000) { + Steps nodeSub = new Steps(sub, temp.steps + 1); + queue.offer(nodeSub); + } + } + return -1; + } + + public static void main(String[] args) { + // int x = 2, y = 5; + int x = 4, y = 7; + System.out.println(minOperations(x, y)); + } + + static class Steps { + int val; + int steps; + + public Steps(int val, int steps) { + this.val = val; + this.steps = steps; + } + } +} + + diff --git a/src/geeksforgeeks/CountAllPathsFrom2DMatrix.java b/src/main/java/practiceproblems/CountAllPathsFrom2DMatrix.java similarity index 87% rename from src/geeksforgeeks/CountAllPathsFrom2DMatrix.java rename to src/main/java/practiceproblems/CountAllPathsFrom2DMatrix.java index dd13894..334adb5 100644 --- a/src/geeksforgeeks/CountAllPathsFrom2DMatrix.java +++ b/src/main/java/practiceproblems/CountAllPathsFrom2DMatrix.java @@ -1,19 +1,19 @@ -package geeksforgeeks; +package practiceproblems; /*https://www.geeksforgeeks.org/count-possible-paths-top-left-bottom-right-nxm-matrix/*/ class CountAllPathsFrom2DMatrix { // A Java program to count all possible paths -// from top left to bottom right + // from top left to bottom right // Returns count of possible paths to reach // cell at row number m and column number n from // the topmost leftmost cell (cell at 1, 1) static int numberOfPaths(int m, int n) { // Create a 2D table to store results // of subproblems - int count[][] = new int[m][n]; + int[][] count = new int[m][n]; // Count of paths to reach any cell in - // first column is 1 + // first row is 1 for (int i = 0; i < m; i++) count[i][0] = 1; @@ -29,7 +29,7 @@ static int numberOfPaths(int m, int n) { for (int j = 1; j < n; j++) // By uncommenting the last part the - // code calculatest he total possible paths + // code calculates the total possible paths // if the diagonal Movements are allowed count[i][j] = count[i - 1][j] + count[i][j - 1]; //+ count[i-1][j-1]; } diff --git a/src/main/java/practiceproblems/CountAndSay.java b/src/main/java/practiceproblems/CountAndSay.java new file mode 100644 index 0000000..dcf4a3a --- /dev/null +++ b/src/main/java/practiceproblems/CountAndSay.java @@ -0,0 +1,42 @@ +package practiceproblems; + + +/** + * https://leetcode.com/problems/count-and-say/ + * tricky + */ +public class CountAndSay { + public String countAndSay(int n) { + if (n <= 0) { + return "-1"; + } + String result = "1"; + + for (int i = 1; i < n; i++) { + result = build(result); + } + return result; + } + + private String build(String result) { + StringBuilder builder = new StringBuilder(); + int p = 0; + while (p < result.length()) { + char val = result.charAt(p); + int count = 0; + + while (p < result.length() && result.charAt(p) == val) { // note that p and val will be same in first run, to count single instance + p++; + count++; + } + builder.append(count); + builder.append(val); + } + return builder.toString(); + } + + public static void main(String[] args) { + CountAndSay countAndSay = new CountAndSay(); + System.out.println(countAndSay.countAndSay(4)); + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/CountBinaryStrings.java b/src/main/java/practiceproblems/CountBinaryStrings.java new file mode 100644 index 0000000..285a0e1 --- /dev/null +++ b/src/main/java/practiceproblems/CountBinaryStrings.java @@ -0,0 +1,77 @@ +package practiceproblems; + +/** + * https://leetcode.com/problems/count-binary-substrings + * tricky + */ +public class CountBinaryStrings { + + /** + * The problem can be solved by observing the examples carefully - + * 1. 0011 + * In this string, consecutive count of binary characters are [2, 2]. We can form a total of 2 substrings. + * 2. 00011 + * In this string, consecutive count of binary characters are [3, 2]. Still, we can only form 2 substrings. + * 3. 000111 + * Here, consecutive count of binary characters are as - [3,3]. Now, we can form 3 substrings. + * 4. 00011100 + * Consecutive count of binary characters are - [3,3,2]. We can form 3 substrings with the first 2 groups of zeros and ones. + * Then we can form 2 substrings with the latter 2 groups. So, a total of 5 substrings. + * 5. 00011100111 + * Consecutive count - [3,3,2,3]. Substrings formed - 3 + 2 + 2 = 7 + * We can observe from the above examples that our final count will only depend on the consecutive counts of binary characters. + * With each two groups of consecutive characters, the number of substrings that can be formed + * will be minimum of count among the two groups. + */ + public int countBinarySubstrings(String s) { + + int i = 1; + int prevCount = 0; + int currCount = 1; + int result = 0; + + while (i < s.length()) { + + if (s.charAt(i - 1) != s.charAt(i)) { + /** + * s[i] != s[i - 1] :Whenever current character is not equal to previous - + * We know that we atleast have group of 2 different characters having consecutiveCount >= 1. + * The number of substrings that can be formed from these would be equal to minimum of currentConsecutiveCount & prevConsecutiveCount. + * So just add that amount to ans. Now prevConsecutiveCount will become equal to + * currentConsecutiveCount and reset the currentConsecutiveCount to 1. + */ + result += Math.min(prevCount, currCount); + prevCount = currCount; + currCount = 1; + } else { + //s[i] == s[i - 1] : When current character is equal to previous - just increment the current consecutive count. + currCount++; + } + i++; + } + + result += Math.min(prevCount, currCount); + return result; + } + + public int countBinarySubstringsExtraSpace(String s) { + //We can convert the string s into an array groups that represents the length of same-character contiguous blocks + // within the string. For example, if s = "110001111000000", then groups = [2, 3, 4, 6]. + int[] groups = new int[s.length()]; + int t = 0; + groups[0] = 1; + for (int i = 1; i < s.length(); i++) { + if (s.charAt(i-1) != s.charAt(i)) { + groups[++t] = 1; + } else { + groups[t]++; + } + } + + int ans = 0; + for (int i = 1; i <= t; i++) { + ans += Math.min(groups[i-1], groups[i]); + } + return ans; + } +} diff --git a/src/main/java/practiceproblems/CountElements.java b/src/main/java/practiceproblems/CountElements.java new file mode 100644 index 0000000..1fafaf8 --- /dev/null +++ b/src/main/java/practiceproblems/CountElements.java @@ -0,0 +1,30 @@ +package practiceproblems; + + +/** + * https://leetcode.com/problems/counting-elements/ + * Given an integer array arr, count how many elements x there are, such that x + 1 is also in arr. + * If there are duplicates in arr, count them separately. + * Input: arr = [1,3,2,3,5,0] + * Output: 3 + * Explanation: 0, 1 and 2 are counted cause 1, 2 and 3 are in arr. + */ +public class CountElements { + + public static int countElements(int[] arr) { + int[] freq = new int[1002]; + for(int num : arr) { + freq[num]++; + } + + int res = 0; + for(int num : arr) { + if (freq[num + 1] > 0) res++; + } + return res; + } + + public static void main(String[] args) { + System.out.println(countElements(new int[]{1, 3, 2, 3, 5, 0})); + } +} \ No newline at end of file diff --git a/src/geeksforgeeks/CountMinimumStepsToFormDesiredInputArray.java b/src/main/java/practiceproblems/CountMinimumStepsToFormDesiredInputArray.java similarity index 75% rename from src/geeksforgeeks/CountMinimumStepsToFormDesiredInputArray.java rename to src/main/java/practiceproblems/CountMinimumStepsToFormDesiredInputArray.java index b36b8fa..1abf2aa 100644 --- a/src/geeksforgeeks/CountMinimumStepsToFormDesiredInputArray.java +++ b/src/main/java/practiceproblems/CountMinimumStepsToFormDesiredInputArray.java @@ -1,7 +1,9 @@ -package geeksforgeeks;/* Java program to count minimum number of operations -to get the given arr array */ +package practiceproblems; + +/** + * https://www.geeksforgeeks.org/count-minimum-steps-get-given-desired-array/ + */ -/*https://www.geeksforgeeks.org/count-minimum-steps-get-given-desired-array/*/ // unsolved class CountMinimumStepsToFormDesiredInputArray { static int arr[] = new int[]{16, 16, 16}; @@ -25,17 +27,20 @@ static int countMinOperations(int n) { int i; // To find first odd element for (i = 0; i < n; i++) { // If odd number found - if (arr[i] % 2 == 1) + if (arr[i] % 2 == 1) { break; + } - // If 0, then increment zero_count - else if (arr[i] == 0) + // If 0, then increment zero_count + else if (arr[i] == 0) { zero_count++; + } } // All numbers are 0 - if (zero_count == n) + if (zero_count == n) { return result; + } // All numbers are even if (i == n) { @@ -59,9 +64,9 @@ else if (arr[i] == 0) public static void main(String[] args) { - System.out.println("Minimum number of steps required to \n" + - "get the given target array is " + - countMinOperations(arr.length)); + System.out.println( + "Minimum number of steps required to \n" + "get the given target array is " + countMinOperations( + arr.length)); } } diff --git a/src/geeksforgeeks/DivideSubArrayAverage.java b/src/main/java/practiceproblems/DivideSubArrayAverage.java similarity index 67% rename from src/geeksforgeeks/DivideSubArrayAverage.java rename to src/main/java/practiceproblems/DivideSubArrayAverage.java index 72ee60e..9ca26e8 100644 --- a/src/geeksforgeeks/DivideSubArrayAverage.java +++ b/src/main/java/practiceproblems/DivideSubArrayAverage.java @@ -1,7 +1,18 @@ -package geeksforgeeks; +package practiceproblems; /** * https://www.geeksforgeeks.org/divide-array-two-sub-arrays-averages-equal/ + * + * Given an integer array, the task is to divide an integer array into + * two sub-arrays to make their averages equal if possible. + * + * Input : arr[] = {1, 5, 7, 2, 0}; + * Output : (0 1) and (2 4) + * Subarrays arr[0..1] and arr[2..4] have + * same average. + * + * Input : arr[] = {4, 3, 5, 9, 11}; + * Output : Not possible */ class DivideSubArrayAverage { @@ -29,7 +40,7 @@ static void findSubarrays(int arr[], int n) { } public static void main(String[] args) { - int[] arr = {1, 5, 7, 2, 0}; + int[] arr = { 1, 5, 7, 2, 0 }; int n = arr.length; findSubarrays(arr, n); } diff --git a/src/main/java/practiceproblems/DoubledPairArray.java b/src/main/java/practiceproblems/DoubledPairArray.java new file mode 100644 index 0000000..5059083 --- /dev/null +++ b/src/main/java/practiceproblems/DoubledPairArray.java @@ -0,0 +1,108 @@ +package practiceproblems; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * https://leetcode.com/problems/array-of-doubled-pairs + * + * tricky offset/sort + */ +public class DoubledPairArray { + /** + * Question is essentially asking to find a pair of doubles, in question they twisted it by providing a formula + * arr[2 * i + 1] = 2 * arr[2 * i]. + * What the formula essentially means is find a pair for given A[i] + * + * if 2i == j or i == 2j (reverse to accommodate negatives) then we have solution + * + * The edge case here is that the elements may be negative in the array + * + * We greedily process elements starting from the smallest value, WHY smallest value but not an arbitrary value? + * + * Because since it's the smallest values, let say x, there is only one choice to pair with x: + * If x is a positive number, then it pairs with y = x*2, for example: x = 4 pair with y = 8. + * If x is a non-positive number, then it pairs with y = x/2, for example: x = -8 pair with y = -4. + * (for arr = [-2, -1], -2 is the smallest, this need to be processed first, then it returns valid.) + * + * If there is no corresponding y then it's IMPOSSIBLE, return FALSE. + * If it's an arbitrary value, let say x, there are two choices, + * either x/2 or x*2 is also a good pairing with x (no matter if x is a possible or negative number), + * if we choose x/2 or x*2 to pair with x, it maybe WRONG, because some other elements may need it to make pair. + * + * For example: arr = [2, 4, 1, 8] + * If we process x = 2 first, then there are 2 choices, + * either 4 or 1 can be paired with 2, if we choose 4 -> we got WRONG ANSWER. + * + * Because 8 needs 4, so 2 should be paired with 1. + * So we need to sort our arr array first. + * + * When a pair of (x and y) match, we need to decrease their count. + * So we need to use a HashTable data structure to count the frequency of elements in the arr array. + * + * @param arr + * @return + */ + public static boolean canReorderDoubled(int[] arr) { + Arrays.sort(arr); + var map = new HashMap(); + + for (int x : arr) map.put(x, map.getOrDefault(x, 0) + 1); + + for (int elem : arr) { + + if (map.get(elem) == 0) continue; + + if (elem < 0 && elem % 2 != 0) return false; + + int value = elem < 0 ? elem / 2 : elem * 2; + + if (map.getOrDefault(value, 0) == 0) return false; + + map.put(elem, map.get(elem) - 1); + map.put(value, map.get(value) - 1); + + } + + return true; + } + + public static void main(String[] args) { + canReorderDoubled(new int[]{4,-2,2,-4}); + } + + /** + * The same approach as above but without sorting + */ + public static boolean canReorderDoubledWithoutSorting(int[] A) { + int offset=100000; + // Count the frequency of each number + int[] freq= new int[2*offset]; + for (int n: A) freq[n+offset]++; // n+offset is to accommodate negative values inside array + // -10 becomes 99990 + + // deal with "0", there must be even 0s in array + if (freq[0]%2==1) return false; + + // from -100000 to 100000 + for (int i=0; i<2*offset; i++){ + // if there's no current number left, skip + if (freq[i]==0) continue; + + // otherwise, calculate current number, and it's pair number, then get their frequencies + int cur= i-offset; + int next= cur<0?cur/2:cur*2; + + int curCnt= freq[i]; + int nextCnt= freq[next+offset]; + + // if not enough pair number left, return false + if (nextCnt= 0; i--) { + dp[i][n - 1] = Math.max(1, dp[i + 1][n - 1] - dungeon[i][n - 1]); + } + + // Populate the last row + for (int i = n - 2; i >= 0; i--) { + dp[m - 1][i] = Math.max(1, dp[m - 1][i + 1] - dungeon[m - 1][i]); + } + + // to achieve the answer, we need to setup the last row and last column + // we know to reach last cell we need 6 as energy, let's say that comes + // from cell above it, that cell's original val is +1, so we must have + // 5 energy when we reach there and adding it up, it became 6 + // the reason to put 1 on last row is, the value in that cell is 30 + // so to reach last cell from that cell, we need only 6 energy(min) + // to have 6 from +30, player should have health of -24 and player cannot + // have neg val, so we put 1 as filler + // |* * 2 | + // |* * 5 | + // |1 1 6 | + + // Populate the rest by taking max of bottom and right (reverse of down and left) + + for (int i = m - 2; i >= 0; i--) { + for (int j = n - 2; j >= 0; j--) { + dp[i][j] = Math.max(1, Math.min(dp[i + 1][j], dp[i][j + 1]) - dungeon[i][j]); + } + } + + return dp[0][0]; + } + + /** + * tricky + */ + public int calculateMinimumHPRecursion(int[][] dungeon) { + int height = dungeon.length; + int width = dungeon[0].length; + // minimal health required to reach this point + Integer[][] minHealth = new Integer[height][width]; + + return calculateMinHealth(0, 0, dungeon, minHealth); + } + + private int calculateMinHealth(int i, int j, int[][] dungeon, Integer[][] minHealth) { + // base case + if (i == (dungeon.length - 1) && j == (dungeon[0].length - 1)) { + return dungeon[i][j] >= 0 ? 1 : (1 - dungeon[i][j]); + } + + // transition + // corner case, i or j out of bound, return very large number, that will never be used + if (i == dungeon.length || j == dungeon[0].length) { + return Integer.MAX_VALUE; + } + + // check cache + // min health requires at least 1 + if (minHealth[i][j] != null) { + return minHealth[i][j]; + } + + // real transition, from right or bottom + int rightMinHealth = calculateMinHealth(i, j + 1, dungeon, minHealth); + int bottomMinHealth = calculateMinHealth(i + 1, j, dungeon, minHealth); + int currentRequiredHealth = Math.min(rightMinHealth, bottomMinHealth) - dungeon[i][j]; + // update cache + minHealth[i][j] = currentRequiredHealth <= 0 ? 1 : currentRequiredHealth; + + return minHealth[i][j]; + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/DutchNationalFlag.java b/src/main/java/practiceproblems/DutchNationalFlag.java new file mode 100644 index 0000000..4255483 --- /dev/null +++ b/src/main/java/practiceproblems/DutchNationalFlag.java @@ -0,0 +1,39 @@ +package practiceproblems; + +/** + * https://leetcode.com/problems/sort-colors/ + */ +class DutchNationalFlag { + public void sortColors(int[] arr) { + if (arr.length == 0) { + return; + } + int low = 0; + int mid = 0; + int high = arr.length - 1; + + while (mid <= high) { + if (arr[mid] == 0) { + //whatever comes from the low index has already been processed, + // so it's safe to increment both low and mid + swap(arr, low, mid); + low++; + mid++; + } else if (arr[mid] == 1) { + mid++; + } else { + swap(arr, mid, high); + //The reason we don't increment mid in this case is that after the swap, + // we're not sure what value is now at arr[mid]. + // It could be 0, 1, or 2. We need to examine this new value in the next iteration. + high--; + } + } + } + + public void swap(int[] arr, int i, int j) { + int temp = arr[j]; + arr[j] = arr[i]; + arr[i] = temp; + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/EvaluateRPN.java b/src/main/java/practiceproblems/EvaluateRPN.java new file mode 100644 index 0000000..7ce3b85 --- /dev/null +++ b/src/main/java/practiceproblems/EvaluateRPN.java @@ -0,0 +1,41 @@ +package practiceproblems; + +import java.util.ArrayDeque; + +/** + * https://leetcode.com/problems/evaluate-reverse-polish-notation + */ +public class EvaluateRPN { + public int evalRPN(String[] tokens) { + ArrayDeque s = new ArrayDeque<>(); + + for (String token : tokens) { + if ("+-*/".contains(token)) { + int second = s.pop(); + int first = s.pop(); + switch (token) { + case "+": + s.push(second + first); + break; + case "-": + s.push(first - second); + break; + case "*": + s.push(first * second); + break; + default: + s.push(first / second); + break; + } + + } else { + s.push(Integer.parseInt(token)); + } + + } + + return s.pop(); + + + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/Fenwick2D.java b/src/main/java/practiceproblems/Fenwick2D.java new file mode 100644 index 0000000..f7e6be1 --- /dev/null +++ b/src/main/java/practiceproblems/Fenwick2D.java @@ -0,0 +1,74 @@ +package practiceproblems; + +public class Fenwick2D { + + // Instance variables + int[][] tree; // bit tree, sumNums(0->i) will be stored at tree(i+1), tree is reference by Length + int[][] nums; // a deep clone of the input matrix. otherwise matrix might be updated by other process + int m; // num of rows + int n; // num of cols + + // Constructor initialization + public Fenwick2D(int[][] matrix) { + // input checks + if (matrix.length == 0 || matrix[0].length == 0) { + return; + } + // initialize variables + m = matrix.length; + n = matrix[0].length; + tree = new int[m + 1][n + 1]; + // deep clone matrix for reference, to prevent other process change matrix + nums = new int[m][n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + // Another change that happens in the case of 2D bit is, + // the partial sums that we store now are corresponding to + // partial sum of the non-overlapping sub-rectangles or sub-matrix present in the matrix. + update(i, j, matrix[i][j]); + } + } + } + + // Function similar to Map.put(Key, Val), key is (row, col), new value is (val) + public void update(int row, int col, int val) { + // input validation: empty matrix || row col not in range + if (m == 0 || n == 0 || row < 0 || row > m || col < 0 || col > n) { + return; + } + // update cloned matrix: nums + int oldVal = nums[row][col]; + nums[row][col] = val; + // update bit tree with delta + int delta = val - oldVal; + for (int i = row + 1; i <= m; i += i & (-i)) { // remember tree is indexed by rLen & cLen, off-by-one index + for (int j = col + 1; j <= n; j += j & (-j)) { + tree[i][j] += delta; + } + } + } + + // Assume row1 <= row2 and col1 <= col2. both 0-base index and all input within range + public int sumRegion(int row1, int col1, int row2, int col2) { + // input validation: empty matrix || row col not in range + if (m == 0 || n == 0 || row1 < 0 || row1 > m || col1 < 0 || col1 > n || row2 < 0 || row2 > m || col2 < 0 || col2 > n) { + return 0; + } + // used 4 rectangle areas [(0, 0), (x, y)] to compute wanted area + // think about cases where row1 || col1 might be 0 + return sum(row2, col2) + sum(row1 - 1, col1 - 1) - sum(row1 - 1, col2) - sum(row2, col1 - 1); + } + + public int sum(int row, int col) { + // remember tree is indexed by rLen & cLen, off-by-one index + int rLen = row + 1; + int cLen = col + 1; + int sum = 0; + for (int i = rLen; i > 0; i -= i & (-i)) { + for (int j = cLen; j > 0; j -= j & (-j)) { + sum += tree[i][j]; + } + } + return sum; + } +} diff --git a/src/main/java/practiceproblems/FenwickTree.java b/src/main/java/practiceproblems/FenwickTree.java new file mode 100644 index 0000000..29df534 --- /dev/null +++ b/src/main/java/practiceproblems/FenwickTree.java @@ -0,0 +1,116 @@ +package practiceproblems; + +/** + * tricky + * + * https://codeforces.com/blog/entry/61364 + * + * Fenwick trees are binary indexed trees, when asked for a range queries for an array + * ex [1,3,4,1,2] print the sum for range (0,4), (1,2) etc.. sum, difference, product, division can be asked + * one way is to pre-compute the prefix sums like [1,4,8,9,11] + * when asked for sum between 0 to 4 return 9 + * when asked sum between 2 to 4 we can return sum of (0,1) - (0,4) + * the retrieval is O(1), but if the original array is updated we need to compute + * the prefix sum which will take O(N). + * Fenwick tree solves this by making both get and update as O(log n) + */ +public class FenwickTree{ + + // the array size is always incremented by 1, because the first entry is + // a dummy entry, the trees are arranged in set bits of each array's index + // we consider 4 pos of the bit representation + /** + * for array [1,-7,15,9,4,2,0,10] + * 0=> [0/0000] (index/ bit val of index) + * / | \ \ + * 1=> [1/0001] [2/0010] [4/0100] [8/1000] - level of indexes with 1 right set bit (flipping right most bit will get parent) + * / / \ + * 2=> [3/0011] [5/0101] [6/0110] - level of indexes with 2 right set bit (flipping right most bit will get parent) + * / + * 3=> [7/0111] - level of indexes with 3 right set bit (flipping right most bit will get parent) + * + * so from the above tree we can infer that to get to a parent node, we need to + * remove the right most set bit + * [7/0111] => [6/0110]=> [4/0100]=> [0/0000] + * and the formula for that is parent = i - (i & -i); + * + * before populating fenwick tree with values, calculate the prefix sum for the array + * [1,-7,15,9,4,2,0,10]=> [1,-6,9,18,22,24,24,34] and update in its position in fenwick tree + * + * [0/0000] + * / | \ \ + * [1/0001] [-6/0010] [18/0100] [34/1000] + * / / \ + * [9/0011] [22/0101] [24/0110] + * / + * [24/0111] + * then we need to subtract each cell's value to its immediate parent's value, while traversing (through parent )we can add parent's value to + * get the correct ans. + * + * Then the tree would look like + * we can use the above formula parent = i - (i & -i); + * + * [0/0000] + * / | \ \ + * [1/0001] [-6/0010] [18/0100] [34/1000] + * / / \ + * [15/0011] [4/0101] [6/0110] + * / + * [0/0111] + * in array's representation it'd look like + * [0,1,-6,15,18,4,6,0,34] + */ + + int[] fen; int[] arr; + int n; + public void NumArray(int[] nums) { + arr = nums; + n = nums.length; + fen = new int[n+1]; + init(); + } + + public void init(){ + if(n == 0) return; + + fen[1] = arr[0]; + for(int i = 1; i < n; i++){ + fen[i+1] = fen[i] + arr[i]; + } + for(int i = n; i >0 ; i--){ + int parent = i - (i & -i); + if(parent >= 0) fen[i] -= fen[parent]; + } + } + + + + // for update we need to come from parent to children + // to get children do opposite of i-(i&-i) + public void update(int i, int val) { + int extra = val - arr[i]; + arr[i] = val; + increment(i, extra); + } + + public int sum(int i){ + int res = 0; + while(i > 0){ + res += fen[i]; + i = i - (i & -i); + } + return res; + } + public void increment(int i, int val){ + i++; + while(i <= n){ + fen[i] += val; + i = i + (i & -i); + } + } + //sum(1,4)=> sum(0,4)-(0,1) the sum function will calculate from 4->0(parent) + public int sumRange(int i, int j) { + return sum(j+1) - sum(i); + } + +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/FindAllAnagram.java b/src/main/java/practiceproblems/FindAllAnagram.java new file mode 100644 index 0000000..e645967 --- /dev/null +++ b/src/main/java/practiceproblems/FindAllAnagram.java @@ -0,0 +1,50 @@ +package practiceproblems; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * https://leetcode.com/problems/find-all-anagrams-in-a-string/ + */ +public class FindAllAnagram { + + public static List findAnagrams(String s, String p) { + Map map = new HashMap<>(); + for (char ch : p.toCharArray()) { + map.put(ch, map.getOrDefault(ch, 0) + 1); + } + int counter = map.size(); + + int start = 0; + int end = 0; + List result = new ArrayList<>(); + while (end < s.length()) { + char temp = s.charAt(end); + if (map.containsKey(temp)) { + map.put(temp, map.get(temp) - 1); + if (map.get(temp) == 0) counter--; + } + end++; + + while (counter == 0) { + char begin = s.charAt(start); + if (map.containsKey(begin)) { + map.put(begin, map.get(begin) + 1); + if (map.get(begin) > 0) counter++; + } + if (end - start == p.length()) { + result.add(start); + } + start++; + } + } + + return result; + } + + public static void main(String[] args) { + System.out.println(findAnagrams("abbabab", "ab")); + } +} diff --git a/src/main/java/practiceproblems/FindAllPossibleRecipes.java b/src/main/java/practiceproblems/FindAllPossibleRecipes.java new file mode 100644 index 0000000..c16a0da --- /dev/null +++ b/src/main/java/practiceproblems/FindAllPossibleRecipes.java @@ -0,0 +1,147 @@ +package practiceproblems; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * https://leetcode.com/problems/find-all-possible-recipes-from-given-supplies + * + */ +public class FindAllPossibleRecipes { + + + public List findAllRecipesBruteForce(String[] recipes, List> ingredients, String[] supplies) { + Set supply= new HashSet<>(List.of(supplies)); + List result= new ArrayList<>(); + + + for(int i=0;i [sandwich,burger] + * // sandwich -> [burger] ] + * + * enqueue the indegree 0 and start processing + * + * if the indegree 0's reciepe is found in the dependency list we got the missing reciepe, start processing it + */ + public List findAllRecipesTopological(String[] recipes, List> ingredients, String[] supplies) { + // Map for storing the recipes indexes + HashMap recMap = new HashMap<>(); + + // Graph for vertices as the recipes and all the dependent recipes on them + HashMap> graph = new HashMap<>(); + + // Number of recipes + int n = recipes.length; + + // Set of all the supplies we have + HashSet sup = new HashSet<>(); + + // Maintain the list of all the available supplies with us + Collections.addAll(sup, supplies); + + // Maintain the index for all the recipes + for (int i = 0; i < n; i++) { + recMap.put(recipes[i], i); + } + + // indegree for all receipes + int[] indegree = new int[n]; + + // Traverse through all the receipes and the ingredients required by them + for (int i = 0; i < n; i++) { + //[["yeast","flour"],["bread","meat"],["sandwich","meat","bread"]] + //supplies = ["yeast","flour","meat"] + for (String str : ingredients.get(i)) { + + // If one of the ingredient is not present in supply then it is dependent on a recipes + if (!sup.contains(str)) { + // Add this particular receipe as a dependent recipe + //recipes = ["bread","sandwich","burger"] + graph.computeIfAbsent(str, x->new ArrayList<>()).add(recipes[i]); + // [bread -> [sandwich,burger] + // sandwich -> [burger] ] + + // Increment the indegree of the dependent recipe + indegree[i]++; + } + } + } + + // Queue for doing Topological Sorting + Queue q = new LinkedList<>(); + + // All the recipes that have indegree as 0 + for (int i = 0; i < n; i++) { + if (indegree[i] == 0) { + q.add(recipes[i]); + } + } + + // List of all the recipes that can be created + List ans = new ArrayList<>(); + + // Until there are nodes traverse it + while (!q.isEmpty()) { + // Peek a node from the queue + String node = q.poll(); + // Add it to the list of recipes that can be created + ans.add(node); + + // Traverse through all the dependent recipes and add them to queue if indegree is 0 + if (graph.containsKey(node)) { + // [bread -> [sandwich,burger] + // sandwich -> [burger] ] + for (String str : graph.get(node)) { + + indegree[recMap.get(str)]--; + if (indegree[recMap.get(str)] == 0) + q.add(str); + } + } + } + + return ans; + + } + + public static void main(String[] args) { + System.out.println(new FindAllPossibleRecipes().findAllRecipesTopological(new String[]{"bread","sandwich","burger"}, + List.of(List.of("yeast","flour"),List.of("bread","meat"),List.of("sandwich","meat","bread")),new String[]{"yeast","flour","meat"})); + } +} diff --git a/src/main/java/practiceproblems/FindHeaters.java b/src/main/java/practiceproblems/FindHeaters.java new file mode 100644 index 0000000..f87eee0 --- /dev/null +++ b/src/main/java/practiceproblems/FindHeaters.java @@ -0,0 +1,39 @@ +package practiceproblems; + +import java.util.Arrays; + +/** + * https://leetcode.com/problems/heaters/ + * tricky + * + * The difficulty of this problem is to understand Math.abs(heaters[i] - house) >= Math.abs(heaters[i + 1] - house + * Let's us understand this with a example: + * houses: 1, 2, 3, 4 + * heaters: 1, 4 + * For house 1, heater 1 is closer to it than heater 4, so we don't move i to i + 1. + * For house 2, it is same. heater 1 is closer. + * For house 3, it is clear that heater 1 no longer closer, so we move i to i + 1. + * For house 4, continue.... + * + * The idea here is we move the heater to rightward in case it is closer to the given house. + */ +public class FindHeaters { + + public int findRadius(int[] houses, int[] heaters) { + Arrays.sort(houses); + Arrays.sort(heaters); + + int i = 0; + int result = 0; + + for (int house : houses) { + while (i + 1 < heaters.length && Math.abs(house - heaters[i]) >= Math.abs(house - heaters[i + 1])) { + i++; + } + + result = Math.max(result, Math.abs(house - heaters[i])); + } + + return result; + } +} diff --git a/src/main/java/practiceproblems/FindMissingNumbers.java b/src/main/java/practiceproblems/FindMissingNumbers.java new file mode 100644 index 0000000..0ceb9ca --- /dev/null +++ b/src/main/java/practiceproblems/FindMissingNumbers.java @@ -0,0 +1,74 @@ +package practiceproblems; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +// Input: [2, 3, 1, 8, 2, 3, 5, 1] +// Output: 4, 6, 7 +public class FindMissingNumbers { + + //Brute Force + // Set set = new HashSet<>(); + // for (int i = 0; i < nums.length; i++) set.add(i + 1); + // for (int i = 0; i < nums.length; i++) set.remove(nums[i]); + // return new ArrayList<>(set); + public List findDisappearedNumbers(int[] nums) { + if (nums.length == 0) return Collections.emptyList(); + int i = 0; + // cyclic sort begins + while (i < nums.length) { + int j = nums[i] - 1; + if (nums[i] != nums[j]) { + swap(nums, i, j); + } else { + i++; + } + } + // cyclic sort ends + // when cyclic sort ends the i+1 element will be in correct index + List result = new ArrayList<>(); + i = 0; + while (i < nums.length) { + if (i + 1 != nums[i]) result.add(i + 1); + i++; + } + return result; + } + + public void swap(int[] nums, int i, int j) { + int temp = nums[i]; + nums[i] = nums[j]; + nums[j] = temp; + } + + public int missingNumber(int[] nums) { + //using a bit modify version of cyclic sort + int index = 0; + int n = nums.length; + while (index < n) { + int correct = nums[index]; + if (nums[index] < n && nums[index] != nums[correct]) //ignore if value is equal to n + { + int temp = nums[index]; + nums[index] = nums[correct]; + nums[correct] = temp; + + } else { + index++; + } + } + + //now search + for (int i = 0; i < n; i++) { + if (nums[i] != i)//we know every element should equal to its index + { + return i; + } + } + //case 2 , when missing number is not in array + return n; + + + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/FindPairs.java b/src/main/java/practiceproblems/FindPairs.java new file mode 100644 index 0000000..9e493d3 --- /dev/null +++ b/src/main/java/practiceproblems/FindPairs.java @@ -0,0 +1,32 @@ +package practiceproblems; + +import java.util.HashMap; +import java.util.Map; + +/** + * https://leetcode.com/problems/k-diff-pairs-in-an-array/ + */ +public class FindPairs { + + public static int findPairs(int[] nums, int k) { + if (k < 0) return 0; + Map map = new HashMap<>(); + for (int i : nums) { + map.put(i, map.getOrDefault(i, 0) + 1); + } + int count = 0; + for (int m : map.keySet()) { + if (k == 0) { + if (map.get(m) >= 2) count++; + } else { + if (map.containsKey(m + k)) + count++; + } + } + return count; + } + + public static void main(String[] args) { + System.out.println(findPairs(new int[]{3, 1, 4, 1, 5}, 0)); + } +} diff --git a/src/main/java/practiceproblems/FindSmallestInteger.java b/src/main/java/practiceproblems/FindSmallestInteger.java new file mode 100644 index 0000000..fbc7c1e --- /dev/null +++ b/src/main/java/practiceproblems/FindSmallestInteger.java @@ -0,0 +1,58 @@ +package practiceproblems; + +/** + * https://www.geeksforgeeks.org/find-smallest-value-represented-sum-subset-given-array/ + * Given a sorted array (sorted in non-decreasing order) of positive numbers, + * find the smallest positive integer value + * that cannot be represented as sum of elements of any subset of given set. + * Expected time complexity is O(n). + * + * */ +class FindSmallestInteger { + + int findSmallest(int a[], int n) { + int maxPossible = 0; + + if(a.length == 0 || a[0] != 1) + { + return maxPossible + 1; + } + + // we have verified that 1 exists in the array. + maxPossible = 1; + + for(int i=1; i maxPossible + 1) + { + break; + } + + maxPossible += a[i]; + } + + return maxPossible + 1; + } + + public static void main(String[] args) { + FindSmallestInteger small = new FindSmallestInteger(); + int arr1[] = { 1, 3, 4, 5 }; + int n1 = arr1.length; + System.out.println("FINAL RESULT: "+small.findSmallest(arr1, n1)); + + int arr2[] = { 1, 2, 6, 10, 11, 15 }; + int n2 = arr2.length; + System.out.println("FINAL RESULT: "+small.findSmallest(arr2, n2)); + + int arr3[] = { 1, 1, 1, 1 }; + int n3 = arr3.length; + System.out.println("FINAL RESULT: "+small.findSmallest(arr3, n3)); + + int arr4[] = { 1, 1, 3, 4 }; + int n4 = arr4.length; + System.out.println("FINAL RESULT: "+small.findSmallest(arr4, n4)); + + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/FirstMissingPositive.java b/src/main/java/practiceproblems/FirstMissingPositive.java new file mode 100644 index 0000000..dc4ca59 --- /dev/null +++ b/src/main/java/practiceproblems/FirstMissingPositive.java @@ -0,0 +1,97 @@ +package practiceproblems; + + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * https://leetcode.com/problems/first-missing-positive/ + */ +public class FirstMissingPositive { + + public int firstMissingPositive(int[] A) { + int i = 0; + while (i < A.length) { + // same cyclic sort, as missing numbers + if (A[i] == i + 1 || A[i] <= 0 || A[i] > A.length) { + i++; + } else if (A[A[i] - 1] != A[i]) { + swap(A, i, A[i] - 1); + } else { + i++; + } + } + i = 0; + while (i < A.length && A[i] == i + 1) + i++; + return i + 1; + } + + private void swap(int[] A, int i, int j) { + int temp = A[i]; + A[i] = A[j]; + A[j] = temp; + } + + public int firstMissingPositiveWithExtraSpace(int[] nums) { + if (nums == null || nums.length == 0) { + return 1; + } + int length = nums.length; + int[] arr = new int[length + 1]; + for (int i = 0; i < length; i++) { + if (nums[i] <= length && nums[i] > 0) { + arr[--nums[i]] = -1; + } + } + + for (int i = 0; i < arr.length; i++) { + if (arr[i] != -1) { + return ++i; + } + } + return -1; + } + + public List findKMissingPositiveNumberOfSizeK(int[] A, int k) { + int i = 0; + while (i < A.length) { + // same cyclic sort, as missing numbers + if (A[i] == i + 1 || A[i] <= 0 || A[i] > A.length) { + i++; + } else if (A[A[i] - 1] != A[i]) { + swap(A, i, A[i] - 1); + } else { + i++; + } + } + + List missingNumber = new ArrayList<>(); + Set additionalNumber = new HashSet<>(); + + i = 0; + while (i < A.length && missingNumber.size() < k) { + if (i + 1 != A[i]) { + missingNumber.add(i + 1); + additionalNumber.add(A[i]); + } + } + + for (i = 1; missingNumber.size() < k; i++) { + if (!additionalNumber.contains(i + A.length)) { + missingNumber.add(i + A.length); + } + } + + return missingNumber; + } + + public static void main(String[] args) { + int[] arr = {3, 4, -1, 1}; + FirstMissingPositive fmp = new FirstMissingPositive(); + System.out.println(fmp.firstMissingPositive(arr)); + System.out.println(fmp.firstMissingPositiveWithExtraSpace(arr)); + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/FirstNonRepeatedCharacter.java b/src/main/java/practiceproblems/FirstNonRepeatedCharacter.java new file mode 100644 index 0000000..207b363 --- /dev/null +++ b/src/main/java/practiceproblems/FirstNonRepeatedCharacter.java @@ -0,0 +1,26 @@ +package practiceproblems; + +public class FirstNonRepeatedCharacter { + public static int firstUniqChar(String s) { + int freq[] = new int[26]; + for (int i = 0; i < s.length(); i++) + freq[s.charAt(i) - 'a']++; + // loop the same string again to find first occurrence not freq array + for (int i = 0; i < s.length(); i++) + if (freq[s.charAt(i) - 'a'] == 1) { + return i; + } + return -1; + } + + public static void main(String args[]) { + String str = "practiceproblems"; + + int index = firstUniqChar(str); + if (index == -1) { + System.out.print("Either all characters are repeating or string is empty"); + } else { + System.out.print("First non-repeating character" + " is " + str.charAt(index)); + } + } +} diff --git a/src/main/java/practiceproblems/FirstNonRepeatingCharacterStream.java b/src/main/java/practiceproblems/FirstNonRepeatingCharacterStream.java new file mode 100644 index 0000000..d3b3882 --- /dev/null +++ b/src/main/java/practiceproblems/FirstNonRepeatingCharacterStream.java @@ -0,0 +1,110 @@ +package practiceproblems; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * https://www.interviewbit.com/problems/first-non-repeating-character-in-a-stream-of-characters/ + */ +public class FirstNonRepeatingCharacterStream { + Set isSeen = new HashSet<>(); + DLList linkedList = new DLList(); + Map map = new HashMap<>(); + + public String solve(String A) { + StringBuilder sb = new StringBuilder(); + for (String s : A.split("")) { + sb.append(uniqueStream(s)); + } + return sb.toString(); + } + + public String uniqueStream(String A) { + Node node = new Node(A); + + if (isSeen.contains(A) && map.get(A) != null) { + Node temp = map.get(A); + linkedList.remove(temp); + map.put(A, null); + } else if (!isSeen.contains(A)) { + isSeen.add(A); + linkedList.add(node); + map.put(A, node); + } + + return linkedList.head.next.val; + } + + /** + * tricky inner loop + */ + public String solveEfficient(String A) { + // we can use two pointer approach to solve this problem. + // First count the occurences of the characters in the stream in the charCounts array + int[] charCounts = new int[26]; + // Keep a start pointer at the start index which will denote the first non-repeating character, if any + int j = 0; + char[] charArr = A.toCharArray(); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < charArr.length; i++) { + // increment the count of the current character + charCounts[charArr[i] - 'a']++; + // move the start pointer ahead either till the current index + // or until we see another non-repeating character + while (j < i && charCounts[charArr[j] - 'a'] > 1) j++; + // If the character at start index is a non repeating character, add it the string otherwise add '#'' + if (charCounts[charArr[j] - 'a'] == 1) sb.append(charArr[j]); + else sb.append('#'); + } + + return sb.toString(); + } + + public static void main(String[] args) { + String vals = "jpxvxivxkkthvpqhhhjuzhkegnzqriokhsgea"; + FirstNonRepeatingCharacterStream solution = new FirstNonRepeatingCharacterStream(); + System.out.println("ans:: " + solution.solveEfficient(vals)); + } + + class DLList { + Node head; + Node tail; + + public DLList() { + head = new Node("#"); + tail = new Node("#"); + head.next = tail; + tail.prev = head; + } + + public void add(Node node) { + Node prev = tail.prev; + prev.next = node; + node.prev = prev; + + node.next = tail; + tail.prev = node; + } + + public void remove(Node temp) { + Node next = temp.next; + temp.prev.next = next; + next.prev = temp.prev; + } + } + + class Node { + Node prev; + Node next; + String val; + + public Node(String val) { + this.val = val; + } + } +} + + + diff --git a/src/main/java/practiceproblems/Flatten2DVector.java b/src/main/java/practiceproblems/Flatten2DVector.java new file mode 100644 index 0000000..3ee139b --- /dev/null +++ b/src/main/java/practiceproblems/Flatten2DVector.java @@ -0,0 +1,69 @@ +package practiceproblems; + +import java.util.List; + +/* +Thoughts: +As hint indicates: use 2 pointers to hold position. +Use hasNext to validate (x,y) and move x. +Use next() to return (x,y) and move it(regardless of correctness, which is determined by hasNext()) + +Implement an iterator to flatten a 2d vector. + +For example, +Given 2d vector = + +[ + [1,2], + [3], + [4,5,6] +] +By calling next repeatedly until hasNext returns false, +the order of elements returned by next should be: [1,2,3,4,5,6]. + +Understand the problem: +The question itself is very easy to solve. Just several corner cases need to think of: + -- What if the 2d vector contains empty arrays, e.g. [ ], [ ], 1 2 3 ? In this case, the next() should not output anything, but the return type is int. There the hasNext() should be more complicated in which it handles this situation. + -- What if the 2d vector itself is empty? Again, handle it in hasNext() +*/ +public class Flatten2DVector { + private int x; + private int y; + private List> list; + + public Flatten2DVector(List> vec2d) { + if (vec2d == null) { + return; + } + this.x = 0; + this.y = 0; + this.list = vec2d; + } + + public int next() { + int rst = list.get(x).get(y); + // when y(column) reaches end increment row(x) and reset y + if (y + 1 >= list.get(x).size()) { + y = 0; + x++; + } else { + y++; + } + return rst; + } + + public boolean hasNext() { + if (list == null) { + return false; + } + // this condition is to check for empty rows + while (x < list.size() && list.get(x).isEmpty()) { + x++; + y = 0; + } + if (x >= list.size()) { + return false; + } + return y < list.get(x).size(); + } +} diff --git a/src/geeksforgeeks/FlattenLinkedList.java b/src/main/java/practiceproblems/FlattenLinkedList.java similarity index 92% rename from src/geeksforgeeks/FlattenLinkedList.java rename to src/main/java/practiceproblems/FlattenLinkedList.java index 1e2e95c..b5e8414 100644 --- a/src/geeksforgeeks/FlattenLinkedList.java +++ b/src/main/java/practiceproblems/FlattenLinkedList.java @@ -1,5 +1,8 @@ -package geeksforgeeks; +package practiceproblems; +/** + * https://www.techiedelight.com/flatten-linked-list/ + */ class FlattenLinkedList { Node head; @@ -19,7 +22,6 @@ public String toString() { } } - Node mergeIterative(Node a, Node b) { Node dummy = new Node(0); Node result = dummy; @@ -49,8 +51,9 @@ Node mergeIterative(Node a, Node b) { } Node flatten(Node root) { - if (root == null || root.right == null) + if (root == null || root.right == null) { return root; + } root.right = flatten(root.right); @@ -59,7 +62,6 @@ Node flatten(Node root) { return root; } - public static void main(String args[]) { flattenList(); } @@ -120,9 +122,13 @@ void printList() { } Node merge(Node a, Node b) { - if (a == null) return b; + if (a == null) { + return b; + } - if (b == null) return a; + if (b == null) { + return a; + } Node result; diff --git a/src/geeksforgeeks/FlipMaximizeZeroesSubarrayKadane.java b/src/main/java/practiceproblems/FlipMaximizeZeroesSubarrayKadane.java similarity index 50% rename from src/geeksforgeeks/FlipMaximizeZeroesSubarrayKadane.java rename to src/main/java/practiceproblems/FlipMaximizeZeroesSubarrayKadane.java index 1f10903..da38c9b 100644 --- a/src/geeksforgeeks/FlipMaximizeZeroesSubarrayKadane.java +++ b/src/main/java/practiceproblems/FlipMaximizeZeroesSubarrayKadane.java @@ -1,24 +1,32 @@ -package geeksforgeeks; +package practiceproblems; + /** * https://www.geeksforgeeks.org/maximize-number-0s-flipping-subarray/ + *

+ * Problem : flip 1's to 0's so that total no.of 0's in array is maximized */ class FlipMaximizeZeroesSubarrayKadane { - public static int findMaxZeroCount(int arr[], int n) { + public static int findMaxZeroCount(int[] arr, int n) { int zeroCount = 0; - int maxSoFar = 0; - int maxEndingHere = 0; + int maxSoFar = Integer.MIN_VALUE; + int sum = 0; for (int i = 0; i < n; i++) { - if (arr[i] == 0) + if (arr[i] == 0) { zeroCount++; + } int val = (arr[i] == 1) ? 1 : -1; - maxEndingHere = Math.max(val, maxEndingHere + val); - maxSoFar = Math.max(maxSoFar, maxEndingHere); + sum = sum + val; + + if (sum < 0) { + sum = 0; + } + maxSoFar = Math.max(maxSoFar, sum); } maxSoFar = Math.max(0, maxSoFar); @@ -26,8 +34,9 @@ public static int findMaxZeroCount(int arr[], int n) { } public static void main(String[] args) { - int arr[] = {0, 1, 0, 0, 1, 1, 0}; + int[] arr = {0, 1, 0, 0, 1, 1, 0}; System.out.println(findMaxZeroCount(arr, arr.length)); } + } \ No newline at end of file diff --git a/src/geeksforgeeks/FlipZeroesToFormConsecutiveMaximumOnes.java b/src/main/java/practiceproblems/FlipZeroesToFormConsecutiveMaximumOnes.java similarity index 83% rename from src/geeksforgeeks/FlipZeroesToFormConsecutiveMaximumOnes.java rename to src/main/java/practiceproblems/FlipZeroesToFormConsecutiveMaximumOnes.java index 535afdb..0d7697f 100644 --- a/src/geeksforgeeks/FlipZeroesToFormConsecutiveMaximumOnes.java +++ b/src/main/java/practiceproblems/FlipZeroesToFormConsecutiveMaximumOnes.java @@ -1,4 +1,4 @@ -package geeksforgeeks; +package practiceproblems; /** * https://www.geeksforgeeks.org/find-zeroes-to-be-flipped-so-that-number-of-consecutive-1s-is-maximized/ @@ -30,12 +30,11 @@ public static void longestSeq(int[] A, int k) { if (A[left] == 0) { count--; } - left++; } // when we reach here, the window [left..right] contains at-most - // k zeroes and we update max window size and leftmost index + // k zeroes, and we update max window size and leftmost index // of the window if (right - left + 1 > window) { window = right - left + 1; @@ -43,14 +42,15 @@ public static void longestSeq(int[] A, int k) { } } - System.out.println("The longest sequence has length " + window + " from index " + leftIndex + " to " - + (leftIndex + window - 1)); + System.out.println( + "The longest sequence has length " + window + " from index " + leftIndex + " to " + (leftIndex + window + - 1)); } // main function public static void main(String[] args) { int[] A = {1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0}; - int k = 1; + int k = 2; longestSeq(A, k); } diff --git a/src/main/java/practiceproblems/FourSum.java b/src/main/java/practiceproblems/FourSum.java new file mode 100644 index 0000000..2bc532e --- /dev/null +++ b/src/main/java/practiceproblems/FourSum.java @@ -0,0 +1,105 @@ +package practiceproblems; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * https://leetcode.com/problems/4sum-ii/ + * Given four lists A, B, C, D of integer values, + * compute how many tuples (i, j, k, l) there are such that A[i] + B[j] + C[k] + D[l] is zero. + * To make problem a bit easier, all A, B, C, D have same length of N where 0 ≤ N ≤ 500. + * All integers are in the range of -228 to 228 - 1 and the result is guaranteed to be at most 231 - 1. + *

+ * Input: + * A = [ 1, 2] + * B = [-2,-1] + * C = [-1, 2] + * D = [ 0, 2] + *

+ * Output: + * 2 + *

+ * Explanation: + * The two tuples are: + * 1. (0, 0, 0, 1) -> A[0] + B[0] + C[0] + D[1] = 1 + (-2) + (-1) + 2 = 0 + * 2. (1, 1, 0, 0) -> A[1] + B[1] + C[0] + D[0] = 2 + (-1) + (-1) + 0 = 0 + */ +public class FourSum { + + public int fourSumCount(int[] A, int[] B, int[] C, int[] D) { + Map sumMap = new HashMap<>(); + + for (int value : A) { + for (int i : B) { + int sum = value + i; + sumMap.put(sum, sumMap.getOrDefault(sum, 0) + 1); + } + } + + int count = 0; + /** + * Compute all the possible sums of the arrays C and D. + * If the hash map contains the opposite value of the current sum, + * increase the count of four elements sum to 0 by the counter in the map. + */ + for (int i : C) { + for (int j : D) { + int sum = i + j; + count += sumMap.getOrDefault(-sum, 0); + } + } + + return count; + } + + public List> fourSum(int[] nums, int target) { + List> result = new ArrayList<>(); + Arrays.sort(nums); + + for (int i = 0; i < nums.length; i++) { + if (i != 0 && nums[i] == nums[i - 1]) { + continue; + } + for (int j = i + 1; j < nums.length; j++) { + if (j != i + 1 && nums[j] == nums[j - 1]) { + continue; + } + int temp = nums[i] + nums[j]; + int k = j + 1; + int l = nums.length - 1; + while (k < l) { + if (target == temp + nums[k] + nums[l]) { + result.add(Arrays.asList(nums[i], nums[j], nums[k], nums[l])); + k++; + l--; + while (k < l && nums[k] == nums[k - 1]) k++; + while (k < l && nums[l] == nums[l + 1]) l--; + + } else if (target > temp + nums[k] + nums[l]) { + k++; + while (k < l && nums[k] == nums[k - 1]) k++; + + } else { + l--; + while (k < l && nums[l] == nums[l + 1]) l--; + } + } + } + } + + return result; + } + + public static void main(String[] args) { + int[] A = {1, 2}; + int[] B = {-2, -1}; + int[] C = {-1, 2}; + int[] D = {0, 2}; + + FourSum fs = new FourSum(); + fs.fourSumCount(A, B, C, D); + } +} diff --git a/src/main/java/practiceproblems/FrequencySort.java b/src/main/java/practiceproblems/FrequencySort.java new file mode 100644 index 0000000..461214f --- /dev/null +++ b/src/main/java/practiceproblems/FrequencySort.java @@ -0,0 +1,53 @@ +package practiceproblems; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.PriorityQueue; + +// sort the string by character frequency from high to low +public class FrequencySort { + // this could be easily done with priority queue but this is ref for bucket sort + public String frequencySort(String s) { + Map map = new HashMap<>(); + for (char c : s.toCharArray()) + map.put(c, map.getOrDefault(c, 0) + 1); + + List[] bucket = new ArrayList[s.length() + 1]; + + for (char key : map.keySet()) { + int frequency = map.get(key); + if (bucket[frequency] == null) bucket[frequency] = new ArrayList<>(); + bucket[frequency].add(key); + } + + StringBuilder sb = new StringBuilder(); + // since this is max frequency we are iterating from last else we'd go from start + for (int pos = bucket.length - 1; pos >= 0; pos--) + if (bucket[pos] != null) + for (char c : bucket[pos]) + sb.append(String.valueOf(c).repeat(Math.max(0, map.get(c)))); + + return sb.toString(); + } + + public String frequencySortEff(String s) { + + Map map = new HashMap<>(); + for (char c : s.toCharArray()) { + map.put(c, map.getOrDefault(c, 0) + 1); + } + + PriorityQueue> pq = new PriorityQueue<>((a, b) -> b.getValue() - a.getValue()); + pq.addAll(map.entrySet()); + + StringBuilder sb = new StringBuilder(); + while (!pq.isEmpty()) { + Map.Entry e = pq.poll(); + sb.append(String.valueOf(e.getKey()).repeat(Math.max(0, e.getValue()))); + } + + return sb.toString(); + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/FurthestBuildingJump.java b/src/main/java/practiceproblems/FurthestBuildingJump.java new file mode 100644 index 0000000..3c0e793 --- /dev/null +++ b/src/main/java/practiceproblems/FurthestBuildingJump.java @@ -0,0 +1,46 @@ +package practiceproblems; + +import java.util.PriorityQueue; + +/** + * tricky priority queue + */ +public class FurthestBuildingJump { + + public int furthestBuilding(int[] heights, int bricks, int ladders) { + + + PriorityQueue queue = new PriorityQueue<>((a, b) -> Integer.compare(b, a)); + for (int reach = 0; reach < heights.length - 1; reach++) { + + if (heights[reach] >= heights[reach + 1]) continue; + + int diff = heights[reach + 1] - heights[reach]; + + if (diff <= bricks) { + bricks -= diff; + queue.offer(diff); + } else if (ladders > 0) { + + /** + * If, however, bricks has become negative, then we'll need to make bricks positive again by reclaiming some bricks; + * we do this by removing the largest brick allocation from the heap and subtracting 1 from ladders to cover the removed brick allocation. + * This works because one of two cases is true; either there's a previous climb with more bricks to reclaim, + * or we've just added the largest climb onto the max-heap. So when we remove the maximum from the max-heap, + * we'll definitely get at least as many bricks as we just subtracted to make bricks non-negative again. + */ + if (!queue.isEmpty()) { + bricks += queue.poll(); + if (bricks >= diff) { + bricks -= diff; + queue.offer(diff); + } + } + ladders--; + } else { + return reach; + } + } + return heights.length - 1; + } +} diff --git a/src/main/java/practiceproblems/GameOfLife.java b/src/main/java/practiceproblems/GameOfLife.java new file mode 100644 index 0000000..8cacb42 --- /dev/null +++ b/src/main/java/practiceproblems/GameOfLife.java @@ -0,0 +1,64 @@ +package practiceproblems; + +/** + * https://forum.letstalkalgorithms.com/t/game-of-life/516/2 + *

+ * https://leetcode.com/problems/game-of-life/ + */ +public class GameOfLife { + // all eight possible directions + private static final int[][] directions = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}, {1, 1}, {-1, -1}, {-1, 1}, {1, -1}}; + private static final int ALIVE = 1; + private static final int DEAD = 0; + private static final int DEAD_TO_ALIVE = 2; + private static final int ALIVE_TO_DEAD = 3; + + public void gameOfLife(int[][] board) { + // iterate through every cell in this 2D array + for (int r = 0; r < board.length; r++) { + for (int c = 0; c < board[0].length; c++) { + // keep track of the number of alive neighbors + int alive = 0; + + // for each cell, check all possible 8 directions and count the number of alive neighbors + for (int[] direction : directions) { + alive += isAlive(board, r + direction[0], c + direction[1]) ? 1 : 0; + } + + // in case current cell is dead but has 3 live neighbors + if (board[r][c] == DEAD) { + if (alive == 3) { + + //Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction. + board[r][c] = DEAD_TO_ALIVE; + } + } + // in case current cell is alive + else { + //Any live cell with fewer than two live neighbors dies as if caused by under-population. + //Any live cell with more than three live neighbors dies, as if by over-population. + //Any live cell with two or three live neighbors lives on to the next generation. + // in case, only 2 or 3 neighbors are alive + if (alive != 2 && alive != 3) { + board[r][c] = ALIVE_TO_DEAD; + } + } + } + } + + for (int r = 0; r < board.length; r++) { + for (int c = 0; c < board[0].length; c++) { + if (board[r][c] == DEAD_TO_ALIVE) { + board[r][c] = ALIVE; + } else if (board[r][c] == ALIVE_TO_DEAD) { + board[r][c] = DEAD; + } + } + } + } + + private boolean isAlive(int[][] board, int r, int c) { + return r >= 0 && r < board.length && c >= 0 && c < board[0].length && (board[r][c] == ALIVE || board[r][c] == ALIVE_TO_DEAD); + } + +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/GrammarMistake.java b/src/main/java/practiceproblems/GrammarMistake.java new file mode 100644 index 0000000..6a40b83 --- /dev/null +++ b/src/main/java/practiceproblems/GrammarMistake.java @@ -0,0 +1,73 @@ +package practiceproblems; + +/** + * https://www.geeksforgeeks.org/check-given-sentence-given-set-simple-grammer-rules/ + * A simple sentence if syntactically correct if it fulfills given rules. The following are given rules. + * 1. Sentence must start with a Uppercase character (e.g. Noun/ I/ We/ He etc.) + * 2. Then lowercase character follows. + * 3. There must be spaces between words. + * 4. Then the sentence must end with a full stop(.) after a word. + * 5. Two continuous spaces are not allowed. + * 6. Two continuous upper case characters are not allowed. + * 7. However, the sentence can end after an upper case character. + */ + +import java.util.Arrays; +import java.util.List; + +public class GrammarMistake { + public static boolean validateSentence(char[] chars) { + int index = 0; + if (Character.isLowerCase(chars[index])) { // 1st condition + return false; + } + + while (index < chars.length) { + if (Character.isUpperCase(chars[index])) { + if (Character.isUpperCase(chars[index + 1])) { // 5th condition + return false; + } + + if (index - 1 >= 0 && chars[index - 1] != ' ') { // 2nd condition + return false; + } + } + + if (chars[index] == ' ' && chars[index + 1] == ' ') { // 4th condition + return false; + } + + index++; + } + + // 3th condition + return chars[index - 2] != ' ' && chars[index - 1] == '.'; + } + + public static void main(String[] args) { + List list = Arrays.asList( + "This sentence is syntactically correct.", + + "This sentence is syntactically incorrect as two " + + "continuous spaces are not allowed.", + + "This sentence is syntactically correct Y.", + + "This sentence is syntactically incorRect as uppercase " + + "character is not allowed midway of the String.", + + "THis sentence is syntactically incorrect as lowercase " + + "character don't follow the first uppercase character.", + + "This sentence is syntactically incorrect as it doesn't " + + "end with a full stop" + ); + + System.out.println("Valid sentences are -"); + for (String sentence : list) { + if (validateSentence(sentence.toCharArray())) { + System.out.println(sentence); + } + } + } +} diff --git a/src/geeksforgeeks/GraphSplitwiseSimplify.java b/src/main/java/practiceproblems/GraphSplitwiseSimplify.java similarity index 88% rename from src/geeksforgeeks/GraphSplitwiseSimplify.java rename to src/main/java/practiceproblems/GraphSplitwiseSimplify.java index 068b8b4..21f24e9 100644 --- a/src/geeksforgeeks/GraphSplitwiseSimplify.java +++ b/src/main/java/practiceproblems/GraphSplitwiseSimplify.java @@ -1,4 +1,4 @@ -package geeksforgeeks; +package practiceproblems; import java.util.HashMap; import java.util.LinkedList; @@ -23,24 +23,16 @@ static class Graph { LinkedList[] adjNodesList; static class AdjNode { - int adjV; + int adjVertices; int debt; - public AdjNode(int adjV, int debt) { - this.adjV = adjV; + public AdjNode(int adjVertices, int debt) { + this.adjVertices = adjVertices; this.debt = debt; } - @Override - public boolean equals(Object o) { - if (this.adjV == ((AdjNode) o).adjV) - return true; - else - return false; - } - public String toString() { - return adjV + "->" + debt; + return adjVertices + "->" + debt; } } @@ -48,7 +40,7 @@ public Graph(int v) { this.V = v; adjNodesList = new LinkedList[V]; for (int i = 0; i < V; i++) { - adjNodesList[i] = new LinkedList(); + adjNodesList[i] = new LinkedList<>(); } } @@ -67,7 +59,7 @@ public void simplifyDebts() { for (int i = 0; i < V; i++) { for (AdjNode adjNode : adjNodesList[i]) { debts[i] -= adjNode.debt; - debts[adjNode.adjV] += adjNode.debt; + debts[adjNode.adjVertices] += adjNode.debt; } } for (int i = 0; i < V; i++) { @@ -106,7 +98,7 @@ public void printDebts() { System.out.println("depts are: "); for (int i = 0; i < V; i++) { for (AdjNode adjNode : adjNodesList[i]) { - System.out.println(i + " owes " + adjNode.adjV + " " + adjNode.debt + " bucks."); + System.out.println(i + " owes " + adjNode.adjVertices + " " + adjNode.debt + " bucks."); } } } diff --git a/src/main/java/practiceproblems/GroupAnagrams.java b/src/main/java/practiceproblems/GroupAnagrams.java new file mode 100644 index 0000000..159dd50 --- /dev/null +++ b/src/main/java/practiceproblems/GroupAnagrams.java @@ -0,0 +1,21 @@ +package practiceproblems; + +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[] strs) { + Map> map = new HashMap<>(); + for (String s : strs) { + char[] temp = s.toCharArray(); + Arrays.sort(temp); + String s1 = new String(temp); + map.putIfAbsent(s1, new ArrayList<>()); + map.get(s1).add(s); + } + return new ArrayList<>(map.values()); + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/GroupIsomorphicString.java b/src/main/java/practiceproblems/GroupIsomorphicString.java new file mode 100644 index 0000000..a73d7a9 --- /dev/null +++ b/src/main/java/practiceproblems/GroupIsomorphicString.java @@ -0,0 +1,57 @@ +package practiceproblems; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * tricky- string hash + */ +public class GroupIsomorphicString { + public Collection> groupIsomorphicStrings(List strings) { + if (strings == null || strings.isEmpty()) { + return Collections.emptyList(); + } + + Map> hashToList = new HashMap<>(); + + for (String string : strings) { + String hash = hash(string); + hashToList.putIfAbsent(hash, new ArrayList<>()); + hashToList.get(hash).add(string); + } + return hashToList.values(); + } + + // this method returns a hash value for every string passed in + // apple = 12234 + // apply = 12234 + // dog = 123 + // cog = 123 + // romi = 1234 + private String hash(String s) { + if (s.isEmpty()) { + return ""; + } + int count = 1; + StringBuilder hash = new StringBuilder(); + + Map map = new HashMap<>(); + + for (char c : s.toCharArray()) { + map.putIfAbsent(c, count++); + hash.append(map.get(c)); + } + return hash.toString(); + } + + public static void main(String[] args) { + Collection> result = new GroupIsomorphicString() + .groupIsomorphicStrings(Arrays.asList("apple", "apply", "dog", "cog", "romi")); + result.stream().forEach(System.out::println); + } +} diff --git a/src/main/java/practiceproblems/HappyNumber.java b/src/main/java/practiceproblems/HappyNumber.java new file mode 100644 index 0000000..7d0bd52 --- /dev/null +++ b/src/main/java/practiceproblems/HappyNumber.java @@ -0,0 +1,56 @@ +package practiceproblems; + +import java.util.HashSet; +import java.util.Set; + +/** + * https://leetcode.com/problems/happy-number/ + */ +class HappyNumber { + public boolean isHappy(int n) { + if (n == 1) return true; + + Set seen = new HashSet<>(); + + while (true) { + int result = 0; + while (n > 0) { + int temp = n % 10; + result += temp * temp; + n /= 10; + } + if (!seen.add(result)) return false; + if (result == 1) break; + seen.add(result); + n = result; + } + + return true; + } + + public int getNext(int n) { + int res = 0; + while (n > 0) { + int t = n % 10; + res += t * t; + n /= 10; + } + return res; + } + + public boolean isHappyOpt(int n) { + int slowRunner = n; + int fastRunner = getNext(n); + + while (fastRunner != 1 && slowRunner != fastRunner) { + slowRunner = getNext(slowRunner); + fastRunner = getNext(getNext(fastRunner)); + } + return fastRunner == 1; + } + + public static void main(String[] args) { + HappyNumber hn = new HappyNumber(); + System.out.println(hn.isHappyOpt(19)); + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/HouseRobber.java b/src/main/java/practiceproblems/HouseRobber.java new file mode 100644 index 0000000..d1e481f --- /dev/null +++ b/src/main/java/practiceproblems/HouseRobber.java @@ -0,0 +1,78 @@ +package practiceproblems; + +/** + * https://leetcode.com/problems/house-robber/ + */ +public class HouseRobber { + + public int rob(int[] nums) { + if (nums.length == 0) { + return 0; + } + int incl = nums[0]; // max money can get if rob current house + int excl = 0; // max money can get if not rob current house + for (int i = 1; i < nums.length; i++) { + int temp = incl; + incl = Math.max(incl, excl + nums[i]); + excl = temp; + } + return incl; + } + + public int robCircular(int[] nums) { + + if (nums.length == 0) return 0; + if (nums.length == 1) return nums[0]; + return Math.max(helperFn(nums, 0, nums.length - 2), helperFn(nums, 1, nums.length - 1)); + + } + + public int helperFn(int[] nums, int start, int end) { + int pre = 0; + int cur = 0; + for (int i = start; i <= end; i++) { + int temp = Math.max(pre + nums[i], cur); + pre = cur; + cur = temp; + + } + return cur; + } + + Integer[] cache; + + public int robBottomUp(int[] nums) { + cache = new Integer[nums.length]; + return recursionHelper(nums, 0); + } + + public int recursionHelper(int[] nums, int index) { + if (index >= nums.length) return 0; + if (cache[index] != null) return cache[index]; + int inclusive = recursionHelper(nums, index + 2) + nums[index]; + int exclusive = recursionHelper(nums, index + 1); + + return cache[index] = Math.max(inclusive, exclusive); + } + + public int robExtraSpace(int[] nums) { + if (nums.length == 1) return nums[0]; + if (nums.length == 2) return Math.max(nums[0], nums[1]); + + int[] dp = new int[nums.length]; + dp[0] = nums[0]; + dp[1] = Math.max(dp[0], nums[1]); + + for (int i = 2; i < nums.length; i++) { + dp[i] = Math.max(dp[i - 2] + nums[i], dp[i - 1]); + } + + return dp[nums.length - 1]; + } + + public static void main(String[] args) { + int[] arr = {2, 7, 9, 3, 1}; + HouseRobber houseRobber = new HouseRobber(); + System.out.println(houseRobber.rob(arr)); + } +} diff --git a/src/main/java/practiceproblems/IPOMaxProfit.java b/src/main/java/practiceproblems/IPOMaxProfit.java new file mode 100644 index 0000000..bd4c80b --- /dev/null +++ b/src/main/java/practiceproblems/IPOMaxProfit.java @@ -0,0 +1,41 @@ +package practiceproblems; + +import java.util.Comparator; +import java.util.PriorityQueue; + +/** + * https://leetcode.com/problems/ipo/ + */ + + +public class IPOMaxProfit { + + // Create (capital, profit) pairs and put them into PriorityQueue pqCap. + // This PriorityQueue sort by capital increasingly. + // Keep polling pairs from pqCap until the project out of current capital capability. Put them into + // PriorityQueue pqPro which sort by profit decreasingly. + // Poll one from pqPro, it's guaranteed to be the project with max profit and within current capital capability. + //Add the profit to capital W. + //Repeat step 2 and 3 till finish k steps or no suitable project (pqPro.isEmpty()). + public int findMaximizedCapital(int steps, int initialCapital, int[] profits, int[] capital) { + + PriorityQueue minQueue = new PriorityQueue<>(Comparator.comparingInt(a -> a[0])); + PriorityQueue maxQueue = new PriorityQueue<>((a, b) -> Integer.compare(b[1], a[1])); + + for (int i = 0; i < profits.length; i++) { + minQueue.offer(new int[]{capital[i], profits[i]}); + } + for (int i = 0; i < steps; i++) { + while (!minQueue.isEmpty() && minQueue.peek()[0] <= initialCapital) { + maxQueue.offer(minQueue.poll()); + } + + if (maxQueue.isEmpty()) break; + + initialCapital += maxQueue.poll()[1]; + + } + return initialCapital; + + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/InOrderSuccessor.java b/src/main/java/practiceproblems/InOrderSuccessor.java new file mode 100644 index 0000000..f2551b6 --- /dev/null +++ b/src/main/java/practiceproblems/InOrderSuccessor.java @@ -0,0 +1,32 @@ +package practiceproblems; + +import trees.TreeNode; + +public class InOrderSuccessor { + /* + * @param root: The root of the BST. + * @param p: You need find the successor node of p. + * @return: Successor of p. + */ + TreeNode result = null; + + public TreeNode inorderSuccessor(TreeNode root, TreeNode p) { + helperFn(root, p); + return result; + } + // Inorder traversal is obtained by going right first and follow the left path till end + // while traversing right we record the right before taking left turn, in case the left path is null + + public void helperFn(TreeNode root, TreeNode p) { + if (root == null) return; + + if (root.val > p.val) { + + result = root; + helperFn(root.left, p); + } else { + helperFn(root.right, p); + } + + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/IntegerToBinary.java b/src/main/java/practiceproblems/IntegerToBinary.java new file mode 100644 index 0000000..1554270 --- /dev/null +++ b/src/main/java/practiceproblems/IntegerToBinary.java @@ -0,0 +1,38 @@ +package practiceproblems; + +class IntegerToBinary { + // function to convert decimal to binary + static void decToBinary(int n) { + // array to store binary number + int[] binaryNum = new int[1000]; + + // counter for binary array + int i = 0; + while (n > 0) { + // storing remainder in binary array + binaryNum[i] = n % 2; + n = n / 2; + i++; + } + + // printing binary array in reverse order + for (int j = i - 1; j >= 0; j--) + System.out.print(binaryNum[j]); + } + + public static void main(String[] args) { + int n = 4; + // decToBinary(n); + // System.out.println("Default method :" + Integer.toBinaryString(4)); + System.out.println(intToBinary(4)); + } + + public static String intToBinary(int n) { + String s = ""; + while (n > 0) { + s = ((n % 2) == 0 ? "0" : "1") + s; + n = n / 2; + } + return s; + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/IntersectionOfArrays.java b/src/main/java/practiceproblems/IntersectionOfArrays.java new file mode 100644 index 0000000..e76b674 --- /dev/null +++ b/src/main/java/practiceproblems/IntersectionOfArrays.java @@ -0,0 +1,106 @@ +package practiceproblems; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +/** + * https://leetcode.com/problems/intersection-of-two-arrays-ii/discuss/82241/AC-solution-using-Java-HashMap + *

+ * Example 1: + *

+ * Input: nums1 = [1,2,2,1], nums2 = [2,2] + * Output: [2,2] + * Example 2: + *

+ * Input: nums1 = [4,9,5], nums2 = [9,4,9,8,4] + * Output: [4,9] + */ +public class IntersectionOfArrays { + public static void main(String[] args) { + IntersectionOfArrays intersectionOfArrays = new IntersectionOfArrays(); + int[] nums1 = {4, 4, 9, 5}; + int[] nums2 = {9, 4, 9, 4, 2, 3}; + System.out.println(Arrays.toString(intersectionOfArrays.intersect(nums1, nums2))); + } + + public int[] intersect(int[] nums1, int[] nums2) { + //The first question is relatively easy, create a hashmap base on number frequency of nums1(whichever one is longer). + + // Then for every element of nums2, look upon the hashmap. If we found an intersection, deduct by 1 to avoid duplicate. + HashMap map = new HashMap<>(); + ArrayList result = new ArrayList<>(); + for (int j : nums1) { + map.put(j, 1); + } + + for (int j : nums2) { + if (map.containsKey(j) && map.get(j) > 0) { + result.add(j); + map.put(j, map.get(j) - 1); + } + } + + int[] r = new int[result.size()]; + for (int i = 0; i < result.size(); i++) { + r[i] = result.get(i); + } + + return r; + } + + //What if the given array is already sorted? How would you optimize your algorithm? + // Classic two pointer iteration, i points to nums1 and j points to nums2. + // Because a sorted array is in ascending order, so if nums1[i] > nums[j], we need to increment j, and vice versa. + // Only when nums1[i] == nums[j], we add it to the result array. Time Complexity O(max(N, M)) + public int[] intersectSorted(int[] nums1, int[] nums2) { + Arrays.sort(nums1); + Arrays.sort(nums2); + int n = nums1.length, m = nums2.length; + int i = 0, j = 0; + List list = new ArrayList<>(); + while (i < n && j < m) { + int a = nums1[i], b = nums2[j]; + if (a == b) { + list.add(a); + i++; + j++; + } else if (a < b) { + i++; + } else { + j++; + } + } + int[] ret = new int[list.size()]; + for (int k = 0; k < list.size(); k++) ret[k] = list.get(k); + return ret; + } + + public List arraysIntersection(int[] arr1, int[] arr2, int[] arr3) { + + List result = new ArrayList<>(); + + int i = 0; + int j = 0; + int k = 0; + + while (i < arr1.length && j < arr2.length && k < arr3.length) { + if (arr1[i] == arr2[j] && arr2[j] == arr3[k]) { + result.add(arr1[i]); + i++; + j++; + k++; + } else if (arr1[i] < arr2[j]) { + i++; + } else if (arr2[j] < arr3[k]) { + j++; + } else { + k++; + } + } + + return result; + + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/IsEditOneDistanceAway.java b/src/main/java/practiceproblems/IsEditOneDistanceAway.java new file mode 100644 index 0000000..2a376ca --- /dev/null +++ b/src/main/java/practiceproblems/IsEditOneDistanceAway.java @@ -0,0 +1,89 @@ +package practiceproblems; + +/** + * https://leetcode.com/problems/one-edit-distance/ + * + * Insert exactly one character into s to get t. + * Delete exactly one character from s to get t. + * Replace exactly one character of s with a different c + * + * Input: s = "ab", t = "acb" + * Output: true + * Explanation: We can insert 'c' into s to get t. + */ +public class IsEditOneDistanceAway { + static boolean isOneEdit(String first, String second) { + // if the input string are same + if (first.equals(second)) + return false; + + int len1 = first.length(); + int len2 = second.length(); + // If the length difference of the stings is more than 1, return false. + if ((len1 - len2) > 1 || (len2 - len1) > 1) { + return false; + } + int i = 0, j = 0; + int diff = 0; + while (i < len1 && j < len2) { + char f = first.charAt(i); + char s = second.charAt(j); + if (f != s) { + diff++; + // delete a character + if (len1 > len2) { + i++; + } + // add a character + if (len2 > len1) { + j++; + } + // replace a character + if (len1 == len2) { + i++; + j++; + } + + } else { + i++; + j++; + } + if (diff > 1) { + return false; + } + } + // If the length of the string is not same. ex. "abc" and "abde" are not one + // edit distance. + return diff != 1 || len1 == len2 || (i == len1 && j == len2); + } + + /* + * There are 3 possibilities to satisfy one edit distance apart: + * + * 1) Replace 1 char: + s: a B c + t: a D c + * 2) Delete 1 char from s: + s: a D b c + t: a b c + * 3) Delete 1 char from t + s: a b c + t: a D b c + * + * tricky substring + */ + public boolean isOneEditDistance(String s, String t) { + for (int i = 0; i < Math.min(s.length(), t.length()); i++) { + if (s.charAt(i) != t.charAt(i)) { + if (s.length() == t.length()) // s has the same length as t, so the only possibility is replacing one char in s and t + return s.substring(i + 1).equals(t.substring(i + 1)); + else if (s.length() < t.length()) // t is longer than s, so the only possibility is deleting one char from t + return s.substring(i).equals(t.substring(i + 1)); + else // s is longer than t, so the only possibility is deleting one char from s + return t.substring(i).equals(s.substring(i + 1)); + } + } + //All previous chars are the same, the only possibility is deleting the end char in the longer one of s and t + return Math.abs(s.length() - t.length()) == 1; + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/IsSubsequence.java b/src/main/java/practiceproblems/IsSubsequence.java new file mode 100644 index 0000000..9e1e4c3 --- /dev/null +++ b/src/main/java/practiceproblems/IsSubsequence.java @@ -0,0 +1,23 @@ +package practiceproblems; + +public class IsSubsequence { + // Input: s = "abc", t = "ahbgdc" + // Output: true + // Input: s = "axc", t = "ahbgdc" + // Output: false + public boolean isSubsequence(String s, String t) { + if (s == null || t == null) return false; + int i = 0; + int j = 0; + + while (i < s.length() && j < t.length()) { + //System.out.println(s.charAt(i)+" - "+t.charAt(j)) + if (s.charAt(i) == t.charAt(j)) { + i++; + } + j++; + } + + return i == s.length(); + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/IsomorphicString.java b/src/main/java/practiceproblems/IsomorphicString.java new file mode 100644 index 0000000..a5abc7f --- /dev/null +++ b/src/main/java/practiceproblems/IsomorphicString.java @@ -0,0 +1,71 @@ +package practiceproblems; + +import java.util.Arrays; + +/** + * The idea is that we need to map a char to another one, for example, "egg" and "add", + * we need to constract the mapping 'e' -> 'a' and 'g' -> 'd'. + * Instead of directly mapping 'e' to 'a', another way is to mark them with same value, + * for example, 'e' -> 1, 'a'-> 1, and 'g' -> 2, 'd' -> 2, this works same. + * So we use two arrays here m1 and m2, initialized space is 256 (Since the whole ASCII size is 256, 128 also works here). + * Traverse the character of both s and t on the same position, + * if their mapping values in m1 and m2 are different, means they are not mapping correctly, + * return false; else we construct the mapping, since m1 and m2 are both initialized as 0, + * we want to use a new value when i == 0, so i + 1 works here. + */ +class IsomorphicString { + static boolean isIsomorphic(String s, String t) { + int[] map1 = new int[256]; + int[] map2 = new int[256]; + Arrays.fill(map1, -1); + Arrays.fill(map2, -1); + + int n = s.length(); + for (int i = 0; i < n; i++) { + char c1 = s.charAt(i); + char c2 = t.charAt(i); + // first time to see the two characters + if (map1[c1] == -1 && map2[c2] == -1) { + map1[s.charAt(i)] = i; + map2[t.charAt(i)] = i; + // one of them is seen before or two are mapping to different + } else if (map1[c1] != map2[c2]) { + return false; + } + + } + return true; + } + + public static boolean isIsomorphicEff(String s, String t) { + int[] mappingDictStoT = new int[256]; + Arrays.fill(mappingDictStoT, -1); + + int[] mappingDictTtoS = new int[256]; + Arrays.fill(mappingDictTtoS, -1); + + for (int i = 0; i < s.length(); ++i) { + char c1 = s.charAt(i); + char c2 = t.charAt(i); + + // Case 1: No mapping exists in either of the dictionaries + if (mappingDictStoT[c1] == -1 && mappingDictTtoS[c2] == -1) { + mappingDictStoT[c1] = c2; + mappingDictTtoS[c2] = c1; + } + + // Case 2: Ether mapping doesn't exist in one of the dictionaries or Mapping exists and + // it doesn't match in either of the dictionaries or both + else if (!(mappingDictStoT[c1] == c2 && mappingDictTtoS[c2] == c1)) { + return false; + } + } + + return true; + } + + public static void main(String[] args) { + System.out.println(isIsomorphicEff("abacb", "xyxzy")); + System.out.println(isIsomorphicEff("paper", "title")); + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/KSmallestPairSum.java b/src/main/java/practiceproblems/KSmallestPairSum.java new file mode 100644 index 0000000..d94c227 --- /dev/null +++ b/src/main/java/practiceproblems/KSmallestPairSum.java @@ -0,0 +1,33 @@ +package practiceproblems; + +import java.util.LinkedList; +import java.util.List; +import java.util.PriorityQueue; + +/** + * tricky priority queue + * https://leetcode.com/problems/find-k-pairs-with-smallest-sums/ + * + * Basic idea: Use min_heap to keep track on next minimum pair sum, and we only need to maintain K possible candidates in the data structure. + * + * Some observations: For every numbers in nums1, its best partner(yields min sum) always strats from nums2[0] since arrays are all sorted; + * And for a specific number in nums1, its next candidate sould be [this specific number] + nums2[current_associated_index + 1], unless out of boundary;) + */ +public class KSmallestPairSum { + public List> kSmallestPairs(int[] nums1, int[] nums2, int k) { + PriorityQueue pq = new PriorityQueue<>((a, b) -> a[0] - b[0]); + for (int i = 0; i < nums1.length && i < k; i++) { + pq.add(new int[]{nums1[i] + nums2[0], i, 0}); + } + List> ans = new LinkedList<>(); + while (!pq.isEmpty() && k > 0) { + int[] peek = pq.remove(); + int i = peek[1], j = peek[2]; + ans.add(List.of(nums1[i], nums2[j])); + if (j < nums2.length - 1) + pq.add(new int[]{nums1[i] + nums2[j + 1], i, j + 1}); + k--; + } + return ans; + } +} diff --git a/src/main/java/practiceproblems/KmostFrequentLetters.java b/src/main/java/practiceproblems/KmostFrequentLetters.java new file mode 100644 index 0000000..9db2041 --- /dev/null +++ b/src/main/java/practiceproblems/KmostFrequentLetters.java @@ -0,0 +1,48 @@ +package practiceproblems; + +import java.util.*; + +/** + * Given a list of reviews, a list of keywords and an integer k. + * Find the most popular k keywords in order of most to least frequently mentioned. + * The comparison of strings is case-insensitive. + * Multiple occurrences of a keyword in a review should be considered as a single mention. + * If keywords are mentioned an equal number of times in reviews, sort alphabetically. + */ +public class KmostFrequentLetters { + public static void main(String[] args) { +// String[] keywords1 = {"anacell", "cetracular", "betacellular"}; +// String[] reviews1 = {"Anacell provides the best services in the city", "betacellular has awesome services", +// "Best services provided by anacell, everyone should use anacell",}; + int k2 = 2; + String[] keywords2 = {"anacell", "betacellular", "cetracular", "deltacellular", "eurocell"}; + String[] reviews2 = {"I love anacell Best services; Best services provided by anacell", + "betacellular has great services", "deltacellular provides much better services than betacellular", + "cetracular is worse than anacell", "Betacellular is better than deltacellular.",}; + System.out.println(solve(k2, keywords2, reviews2)); + } + + private static List solve(int k, String[] keywords, String[] reviews) { + List res = new ArrayList<>(); + Set set = new HashSet<>(Arrays.asList(keywords)); + Map map = new HashMap<>(); + for (String r : reviews) { + String[] strs = r.split("\\W"); + Set added = new HashSet<>(); // creating a set per review to avoid duplicate within a review + for (String s : strs) { + s = s.toLowerCase(); + if (set.contains(s) && !added.contains(s)) { + map.put(s, map.getOrDefault(s, 0) + 1); + added.add(s); + } + } + } + PriorityQueue> maxHeap = new PriorityQueue<>((a, b) -> Objects.equals(a.getValue(), b.getValue()) ? a.getKey().compareTo(b.getKey()) : b.getValue() - a.getValue()); + maxHeap.addAll(map.entrySet()); + map.forEach((key, value) -> System.out.println(key + " " + value)); + while (!maxHeap.isEmpty() && k-- > 0) { + res.add(maxHeap.poll().getKey()); + } + return res; + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/KthCharacterInString.java b/src/main/java/practiceproblems/KthCharacterInString.java new file mode 100644 index 0000000..1ef3706 --- /dev/null +++ b/src/main/java/practiceproblems/KthCharacterInString.java @@ -0,0 +1,41 @@ +package practiceproblems; + +/** + * TODO + * given 2 string s , t we have to find kth character from the string formed by following process lets say x = "" + * i = 1 append s to x 1 time + * i = 2 append t to x 2 times + * i = 3 append s to x 3 times + * i = 4 append t to x 4 times + * i = 5 append s to x 5 times + * and so on + *

+ * you are given k <= 10^16, you have to find kth character from x formed using above process. + * eg: s = "a", t = "bc", k = 4 ---> given input + * Output: b + * (since string x = "abcbcaaabcbcbcbc..... 4th char is b) + */ +public class KthCharacterInString { + + public static char kthCharacter(String s, String t, int k) { + k = k - 1; + for (int i = 1; k >= 0; i++) { + if ((i) % 2 == 1) {//odd s + int currIdxLen1 = (i) * s.length(); + if (currIdxLen1 > k) { // can't reduce k further + return s.charAt(k % s.length()); + } else { + k = k - currIdxLen1; + } + } else { + int currIdxLen = (i) * t.length(); + if (currIdxLen > k) { // can't reduce k further + return t.charAt(k % t.length()); + } else { + k = k - currIdxLen; + } + } + } + return 'x'; + } +} diff --git a/src/main/java/practiceproblems/KthClosestOrigin.java b/src/main/java/practiceproblems/KthClosestOrigin.java new file mode 100644 index 0000000..d1c6701 --- /dev/null +++ b/src/main/java/practiceproblems/KthClosestOrigin.java @@ -0,0 +1,86 @@ +package practiceproblems; + +import java.util.Arrays; +import java.util.PriorityQueue; +import java.util.Random; + +/** + * https://leetcode.com/problems/k-closest-points-to-origin/solution/ + */ +class KthClosestOrigin { + + public int[][] kClosestPQ(int[][] points, int K) { + PriorityQueue pq = new PriorityQueue<>((p1, p2) -> p2[0] * p2[0] + p2[1] * p2[1] - p1[0] * p1[0] - p1[1] * p1[1]); + for (int[] p : points) { + pq.offer(p); + if (pq.size() > K) { + pq.poll(); + } + } + int[][] res = new int[K][2]; + while (K > 0) { + res[--K] = pq.poll(); + } + return res; + } + + + public int[][] kClosest(int[][] points, int k) { + if (k == points.length) { + return points; + } + + int low = 0; + int high = points.length - 1; + int pivotIndex = partition(points, low, high); + + while (pivotIndex != k) { + if (k < pivotIndex) { + high = pivotIndex - 1; + } else { + low = pivotIndex + 1; + } + + pivotIndex = partition(points, low, high); + } + + return Arrays.copyOfRange(points, 0, k); + } + + private int partition(final int[][] points, final int low, final int high) { + if (low >= high) { + return low; + } + + swap(points, low + new Random().nextInt(high - low), high); + + int leftIndex = low; + int rightIndex = high - 1; + + double pivotDistance = distance(points[high]); + + while (leftIndex <= rightIndex) { + if (distance(points[leftIndex]) < pivotDistance) { + leftIndex += 1; + } else { + swap(points, leftIndex, rightIndex); + rightIndex -= 1; + } + } + + swap(points, leftIndex, high); + + return leftIndex; + } + + private void swap(final int[][] points, final int index1, final int index2) { + final int[] temp = points[index1]; + + points[index1] = points[index2]; + points[index2] = temp; + } + + private double distance(final int[] point) { + return Math.sqrt(point[0] * point[0] + point[1] * point[1]); + } +} diff --git a/src/main/java/practiceproblems/KthSmallestMatrix.java b/src/main/java/practiceproblems/KthSmallestMatrix.java new file mode 100644 index 0000000..ddca7ce --- /dev/null +++ b/src/main/java/practiceproblems/KthSmallestMatrix.java @@ -0,0 +1,48 @@ +package practiceproblems; + +import java.util.Comparator; +import java.util.PriorityQueue; + +/** + * https://leetcode.com/problems/kth-smallest-element-in-a-sorted-matrix/ + */ +public class KthSmallestMatrix { + public static int kthSmallest(int[][] matrix, int k) { + PriorityQueue minQueue = new PriorityQueue<>(Comparator.comparingInt(a -> a.value)); + + for (int i = 0; i < matrix[0].length; i++) { + minQueue.offer(new Pair(matrix[0][i], 0, i)); + } + k--; + while (k-- > 0 && !minQueue.isEmpty()) { + Pair temp = minQueue.poll(); + if (temp.x + 1 >= matrix.length) continue; + minQueue.offer(new Pair(matrix[temp.x + 1][temp.y], temp.x + 1, temp.y)); + } + return minQueue.isEmpty() ? -1 : minQueue.peek().value; + } + + public static void main(String[] args) { + int[][] matrix = {{1, 2, 9}, {3, 11, 13}, {4, 13, 15}}; + int k = 4; + + System.out.println(kthSmallest(matrix, k)); + } + + static class Pair { + int value; + int x; + int y; + + public Pair(int value, int x, int y) { + this.value = value; + this.x = x; + this.y = y; + } + + @Override + public String toString() { + return this.value + " "; + } + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/LargestDivisibleSubset.java b/src/main/java/practiceproblems/LargestDivisibleSubset.java new file mode 100644 index 0000000..5d68882 --- /dev/null +++ b/src/main/java/practiceproblems/LargestDivisibleSubset.java @@ -0,0 +1,66 @@ +package practiceproblems; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * Given a set of distinct positive integers, + * find the largest subset such that every pair (Si, Sj) of elements in this subset satisfies: + Si % Sj = 0 or Sj % Si = 0. + If there are multiple solutions, return any subset is fine. + Input: [2,3,4,6,10,8,24] + Output: [2,4,8,24] each pair's modulo is 0 + */ +public class LargestDivisibleSubset { + + /** + * if a%b==0 means a>b, if b>a then the ans is b itself + *inorder to have that we need to sort the array in increasing order + at first each val is ans to itself, then we come from last so a is higher in a%b + for ex if 2 is factor of 4 then include the set involve in available. This is DP problem + [2, 3, 4, 6, 8, 10, 24] + {2} {3} {4} {6} {8} {10} {24} + {8,24} + + {6,24} + + {4,8,24} + + {3,6,24} + + {2,4,8,24} + * + */ + public List largestDivisibleSubset(int[] nums) { + if(nums==null || nums.length==0) return Collections.emptyList(); + + Arrays.sort(nums); + List[] result= new ArrayList[nums.length]; + + int maxLength=0; + int resIndex=-1; + List tempList; + + for(int i=nums.length-1;i>=0;i--){ + result[i]=new ArrayList<>(); // every element is an answer itself + tempList= new ArrayList<>(); + result[i].add(nums[i]); + for(int j=i+1;j tempList.size()){ // this is to take even if 1 element is at j position + tempList=result[j]; // the reason we take list is consider 4,8,24 when i is at 4 and j is 8 mod is 0 means 4%24 is also zero + } + } + } + result[i].addAll(tempList); + if(result[i].size()>maxLength){ + maxLength=result[i].size(); + resIndex=i; + } + } + Collections.sort(result[resIndex]); + return result[resIndex]; + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/LargestPossibleNumber.java b/src/main/java/practiceproblems/LargestPossibleNumber.java new file mode 100644 index 0000000..cb397eb --- /dev/null +++ b/src/main/java/practiceproblems/LargestPossibleNumber.java @@ -0,0 +1,53 @@ +package practiceproblems; + +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +/** + * https://www.geeksforgeeks.org/given-an-array-of-numbers-arrange-the-numbers-to-form-the-biggest-number/ + */ +class LargestPossibleNumber { + + public static void main(String[] args) { + int nums[] = { 10, 68, 97, 9, 21, 12 }; + List numbers = Arrays.asList("10", "68", "97", "9", "21", "12"); + + numbers.sort((a, b) -> (b + a).compareTo(a + b)); + numbers.forEach(System.out::print); + System.out.println(new LargestPossibleNumber().largestNumber(nums)); + } + + public String largestNumber(int[] nums) { + String[] arr = new String[nums.length]; + for (int i = 0; i < nums.length; i++) { + arr[i] = String.valueOf(nums[i]); + } + + Arrays.sort(arr, (a, b) -> (b + a).compareTo(a + b)); + + StringBuilder sb = new StringBuilder(); + for (String s : arr) { + sb.append(s); + } + + while (sb.charAt(0) == '0' && sb.length() > 1) + sb.deleteCharAt(0); + + return sb.toString(); + } + + public String largestNumber1(int[] nums) { + if(nums==null || nums.length==0) return null; + List list= new LinkedList<>(); + for(int i: nums){ + list.add(String.valueOf(i)); + } + + list.sort((a, b) -> (int) (Long.parseLong(b + a) - Long.parseLong(a + b))); + + return String.join("",list).replaceFirst("^0+(?!$)", ""); + + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/LargestSubArrayWithZeroesAndOnes.java b/src/main/java/practiceproblems/LargestSubArrayWithZeroesAndOnes.java new file mode 100644 index 0000000..94e2e70 --- /dev/null +++ b/src/main/java/practiceproblems/LargestSubArrayWithZeroesAndOnes.java @@ -0,0 +1,67 @@ +package practiceproblems; + +import java.util.HashMap; + +/** + * https://www.geeksforgeeks.org/largest-subarray-with-equal-number-of-0s-and-1s/ + * Given an array containing only 0s and 1s, find the largest subarray which contains equal no of 0s and 1s. + * Expected time complexity is O(n). + */ +class LargestSubArrayWithZeroesAndOnes { + /** + * The concept of taking cumulative sum, taking 0’s as -1 will help us in optimising the approach. + * While taking the cumulative sum, there are two cases when there can be a sub-array with equal number of 0’s and 1’s + * When cumulative sum=0, which signifies that sub-array from index (0) till present index has equal number of 0’s and 1’s. + *

+ * When we encounter a cumulative sum value which we have already encountered before, + * which means that sub-array from the previous index+1 till the present index has equal number of 0’s and 1’s as they give a cumulative sum of 0 . + * + */ + int maxLen(int[] arr, int n) { + + HashMap map = new HashMap<>(); + + int sum = 0; + int maxLength = 0; + int endingIndex = -1; + + for (int i = 0; i < n; i++) { + arr[i] = (arr[i] == 0) ? -1 : 1; + } + + for (int i = 0; i < n; i++) { + sum += arr[i]; + + if (sum == 0) { // To handle sum=0 at last index + maxLength = i + 1; + endingIndex = i; + } + // If this sum is seen before, + // then update max_len if required + if (map.containsKey(sum)) { + if (maxLength < i - map.get(sum)) { + maxLength = i - map.get(sum); + endingIndex = i; + } + } else + map.put(sum, i); + } + + for (int i = 0; i < n; i++) { + arr[i] = (arr[i] == -1) ? 0 : 1; + } + + int start = endingIndex - maxLength + 1; + System.out.println(start + " to " + endingIndex); + + return maxLength; + } + + public static void main(String[] args) { + LargestSubArrayWithZeroesAndOnes sub = new LargestSubArrayWithZeroesAndOnes(); + int arr[] = {0, 0, 0, 1, 0, 1, 1}; + int n = arr.length; + + sub.maxLen(arr, n); + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/LargestTimeFromDigits.java b/src/main/java/practiceproblems/LargestTimeFromDigits.java new file mode 100644 index 0000000..f85ae43 --- /dev/null +++ b/src/main/java/practiceproblems/LargestTimeFromDigits.java @@ -0,0 +1,40 @@ +package practiceproblems; + +/** + * TODO + * + * Given an array of 4 digits, return the largest 24 hour time that can be made. + * The smallest 24 hour time is 00:00, and the largest is 23:59. + * Starting from 00:00, a time is larger if more time has elapsed since midnight. + * Return the answer as a string of length 5. If no valid time can be made, return an empty string. + */ +public class LargestTimeFromDigits { + public String largestTimeFromDigits(int[] A) { + if (A == null || A.length == 0) return ""; + String result = ""; + // because A.length == 4 + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + for (int k = 0; k < 4; k++) { + //We cannot take a number twice. i, j, k and (6-i-j-k) denoting the indices of 4 numbers should be distinct. + if (i == j || j == k || k == i) continue; + String hrs = A[i] + "" + A[j]; + //We are trying out all possible ordering, + //indices of 4 numbers are 0, 1, 2, and 3. + //sum of indices = 0 + 1 + 2 + 3= 6 + //i, j and k denote 3 indices. + //So, if we know 3 numbers, + //the 4th number will be the remaining index, i.e., 6-i-j-k + + String mins = A[k] + "" + A[6 - i - j - k]; + + if (hrs.compareTo("24") < 0 && mins.compareTo("59") < 0 && result.compareTo(hrs + ":" + mins) < 0) { + result = hrs + ":" + mins; + } + + } + } + } + return result; + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/LeftMostColumnWithOne.java b/src/main/java/practiceproblems/LeftMostColumnWithOne.java new file mode 100644 index 0000000..be49d9c --- /dev/null +++ b/src/main/java/practiceproblems/LeftMostColumnWithOne.java @@ -0,0 +1,47 @@ +package practiceproblems; + +import java.util.List; + +/** + * A binary matrix means that all elements are 0 or 1. For each individual row of the matrix, this row is sorted in non-decreasing order. + *

+ * Given a row-sorted binary matrix binaryMatrix, return leftmost column index(0-indexed) with at least a 1 in it. If such index doesn’t exist, return -1. + *

+ * You can’t access the Binary Matrix directly. You may only access the matrix using a BinaryMatrix interface: + *

+ * BinaryMatrix.get(row, col) returns the element of the matrix at index (row, col) (0-indexed). + * BinaryMatrix.dimensions() returns a list of 2 elements [rows, cols], which means the matrix is rows * cols. + * Submissions making more than 1000 calls to BinaryMatrix.get will be judged Wrong Answer. + * Also, any solutions that attempt to circumvent the judge will result in disqualification + */ +interface BinaryMatrix { + public int get(int row, int col); + + public List dimensions(); +} + +public class LeftMostColumnWithOne { + + /** + * tricky binary search + * + */ + public int leftMostColumnWithOne(BinaryMatrix binaryMatrix) { + List dimension = binaryMatrix.dimensions(); + int n = dimension.get(0); + int m = dimension.get(1); + + int i = 0, j = m - 1, leftMostOne = -1; // start from 0th row and last column + + while (i < n && j >= 0) { + int result = binaryMatrix.get(i, j); + if (result == 0) + i++; + else { + leftMostOne = j; + j--; + } + } + return leftMostOne; + } +} diff --git a/src/main/java/practiceproblems/LengthOfLongestSubstringKDistinct.java b/src/main/java/practiceproblems/LengthOfLongestSubstringKDistinct.java new file mode 100644 index 0000000..b96dc11 --- /dev/null +++ b/src/main/java/practiceproblems/LengthOfLongestSubstringKDistinct.java @@ -0,0 +1,85 @@ +package practiceproblems; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters + */ +public class LengthOfLongestSubstringKDistinct { + + public static final int CHAR_RANGE = 128; + + public static int lengthOfLongestSubstringKDistinct(String s, int k) { + if (s == null || k == 0) { + return 0; + } + Map map = new HashMap<>(); + + int result = 0; + int left = 0; + int right = 0; + + while (left < s.length()) { + char ch = s.charAt(left); + map.put(ch, map.getOrDefault(ch, 0) + 1); + while (map.size() > k) { + char chright = s.charAt(right); + map.put(chright, map.get(chright) - 1); + if (map.get(chright) <= 0) { + map.remove(chright); + } + right++; + } + left++; + result = Math.max(result, left - right); + } + + return result; + } + + public static void main(String[] args) { + System.out.println(lengthOfLongestSubstringKDistinct("aaaaaa", 2)); + } + + public static String findLongestSubstring(String str, int k) { + // stores the longest substring boundaries + int end = 0, begin = 0; + + // set to store distinct characters in a window + Set window = new HashSet<>(); + + // Count array `freq` stores the frequency of characters present in the + // current window. We can also use a map instead of a count array. + int[] freq = new int[CHAR_RANGE]; + + // `[low…high]` maintains the sliding window boundaries + for (int low = 0, high = 0; high < str.length(); high++) { + window.add(str.charAt(high)); + freq[str.charAt(high)]++; + + // if the window size is more than `k`, remove characters from the left + while (window.size() > k) { + // If the leftmost character's frequency becomes 0 after + // removing it in the window, remove it from the set as well + if (--freq[str.charAt(low)] == 0) { + window.remove(str.charAt(low)); + } + + low++; // reduce window size + } + + // update the maximum window size if necessary + if (end - begin < high - low) { + end = high; + begin = low; + } + } + + // return the longest substring found at `str[begin…end]` + return str.substring(begin, end + 1); + } + +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/LongestConsequtiveSequence.java b/src/main/java/practiceproblems/LongestConsequtiveSequence.java new file mode 100644 index 0000000..aaeee12 --- /dev/null +++ b/src/main/java/practiceproblems/LongestConsequtiveSequence.java @@ -0,0 +1,75 @@ +package practiceproblems; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * Given an unsorted array of integers, + * find the length of the longest consecutive elements sequence. + *

+ * Your algorithm should run in O(n) complexity. + * Input: [100, 4, 200, 1, 3, 2] + * Output: 4 + * Explanation: The longest consecutive elements sequence is [1, 2, 3, 4]. + * Therefore its length is 4. + */ +class LongestConsequtiveSequence { + + public int longestConsecutive(int[] nums) { + if (nums.length == 0) { + return 0; + } + int max = 1; + Set set = new HashSet<>(); + for (int i : nums) { + set.add(i); + } + // have a set, go backwards and remove entries, go forward remove entries and calculate Max + // without removing entries, runtime would be too much + for (Integer i : nums) { + int num = i; + int count = 1; + // looking left + while (set.contains(--num)) { + count++; + set.remove(num); + } + num = i; + while (set.contains(++num)) { + count++; + set.remove(num); + } + + max = Math.max(max, count); + } + + return max; + } + + /** + * tricky traversal array + */ + public int longestConsecutiveSorting(int[] nums) { + if (nums == null || nums.length == 0) + return 0; + + Arrays.sort(nums); + + int longestStreak = 1; + int currentStreak = 1; + + for (int i = 0; i < nums.length - 1; i++) { + if (nums[i] != nums[i + 1]) { // avoid duplicate + if (nums[i] + 1 == nums[i + 1]) { // if increasing then increase streak count else reset + currentStreak += 1; + } else { + longestStreak = Math.max(longestStreak, currentStreak); + currentStreak = 1; + } + } + } + + return Math.max(longestStreak, currentStreak); + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/LongestDiverseString.java b/src/main/java/practiceproblems/LongestDiverseString.java new file mode 100644 index 0000000..649e339 --- /dev/null +++ b/src/main/java/practiceproblems/LongestDiverseString.java @@ -0,0 +1,72 @@ +package practiceproblems; + +import java.util.PriorityQueue; + +/** + * https://leetcode.com/problems/longest-happy-string + * + * Input: a = 1, b = 1, c = 7 + * Output: "ccaccbcc" + * Explanation: "ccbccacc" would also be a correct answer. + */ +public class LongestDiverseString { + public String longestDiverseString(int a, int b, int c) { + StringBuilder builder = new StringBuilder(); + PriorityQueue pq = new PriorityQueue( + (count1, count2) -> Integer.compare(count2.count, count1.count)); + + if (a > 0) + pq.add(new Pair('a', a)); + if (b > 0) + pq.add(new Pair('b', b)); + if (c > 0) + pq.add(new Pair('c', c)); + + while (pq.size() >= 2) { + Pair pair_one = pq.poll(); + if (pair_one.count >= 2) { + builder.append(pair_one.ch); + builder.append(pair_one.ch); + pair_one.count -= 2; + } else { + builder.append(pair_one.ch); + pair_one.count -= 1; + } + + Pair pair_two = pq.poll(); + if (pair_two.count >= 2 && pair_one.count < pair_two.count) { + builder.append(pair_two.ch); + builder.append(pair_two.ch); + pair_two.count -= 2; + } else { + builder.append(pair_two.ch); + pair_two.count -= 1; + } + + if (pair_one.count > 0) + pq.add(pair_one); + if (pair_two.count > 0) + pq.add(pair_two); + } + + if (!pq.isEmpty()) { + if (pq.peek().count >= 2) { + builder.append(pq.peek().ch); + builder.append(pq.peek().ch); + } else { + builder.append(pq.peek().ch); + } + } + return builder.toString(); + } + + static class Pair { + public Character ch; + int count; + + public Pair(Character ch, int count) { + this.ch = ch; + this.count = count; + } + } +} diff --git a/src/main/java/practiceproblems/LongestIncreasingPathInMatrix.java b/src/main/java/practiceproblems/LongestIncreasingPathInMatrix.java new file mode 100644 index 0000000..0768ad0 --- /dev/null +++ b/src/main/java/practiceproblems/LongestIncreasingPathInMatrix.java @@ -0,0 +1,60 @@ +package practiceproblems; + +/** + * tricky dfs + * + * https://leetcode.com/problems/longest-increasing-path-in-a-matrix + *

+ * Do DFS from every cell + * Compare every 4 direction and skip cells that are out of boundary or smaller + * Get matrix max from every cell's max + * Use matrix[x][y] <= matrix[i][j] so we don't need a visited[m][n] array + * The key is to cache the distance because it's highly possible to revisit a cell + *

+ * DFS + Memoization + *

+ * Traverse all points in matrix, use every point as starting point to do dfs traversal. DFS function returns max increasing + * path after comparing four max return distance from four directions. + */ +public class LongestIncreasingPathInMatrix { + + int[][] dirs = new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; + + public int longestIncreasingPath(int[][] matrix) { + if (matrix.length == 0) return 0; + Integer[][] cache = new Integer[matrix.length][matrix[0].length]; + + int result = 0; + for (int i = 0; i < matrix.length; i++) { + for (int j = 0; j < matrix[0].length; j++) { + result = Math.max(dfsUtil(matrix, i, j, cache, Integer.MIN_VALUE), result); + } + } + + return result; + } + + public int dfsUtil(int[][] matrix, int i, int j, Integer[][] cache, int data) { + if (i < 0 || i >= matrix.length || j < 0 || j >= matrix[0].length || data >= matrix[i][j]) return 0; + + + if (cache[i][j] != null) return cache[i][j]; + + // initialize max distance as 1 since the path includes starting point itself + int max = 1; + + for (int[] dir : dirs) { + + int x = i + dir[0]; + int y = j + dir[1]; + // if next point is a valid point, add curLen by 1 and continue DFS traversal + int count = 1 + dfsUtil(matrix, x, y, cache, matrix[i][j]); + max = Math.max(count, max); + } + // update max increasing path value starting from current point in cache + cache[i][j] = max; + + return cache[i][j]; + + } +} diff --git a/src/main/java/practiceproblems/LongestRepeatCharReplace.java b/src/main/java/practiceproblems/LongestRepeatCharReplace.java new file mode 100644 index 0000000..55f6666 --- /dev/null +++ b/src/main/java/practiceproblems/LongestRepeatCharReplace.java @@ -0,0 +1,44 @@ +package practiceproblems; + +/** + * https://leetcode.com/problems/longest-repeating-character-replacement/ + * + * revise + */ +public class LongestRepeatCharReplace { + public int characterReplacement(String s, int k) { + if (s == null || s.length() == 0) return 0; + + int[] cache = new int[26]; + int left = 0; + int right = 0; + int result = 0; + int maxOccurred = 0; + while (right < s.length()) { + char temp = s.charAt(right); + ++cache[temp - 'A']; + maxOccurred = Math.max(maxOccurred, cache[temp - 'A']); + + // end-start+1 = size of the current window + // maxCount = largest count of a single, unique character in the current window + // The main equation is: end-start+1-maxCount + // When end-start+1-maxCount == 0, then the window is filled with only one character + // When end-start+1-maxCount > 0, then we have characters in the window that are NOT the character that occurs the most. + // end-start+1-maxCount is equal to exactly the # of characters that are NOT the character that occurs the most in that window. + //Example: For a window "xxxyz", end-start+1-maxCount would equal 2. (maxCount is 3 and there are 2 characters here, "y" and "z" that are not "x" in the window.) + // We are allowed to have at most k replacements in the window, so when end-start+1-maxCount > k, + //then there are more characters in the window than we can replace, and we need to shrink the window. + // If we have window with "xxxy" and k = 1, that's fine because end-start+1-maxCount = 1, which is not > k. maxLength gets updated to 4. + // But if we then find a "z" after, like "xxxyz", then we need to shrink the window because now end-start+1-maxCount = 2, and 2 > 1. The window becomes "xxyz". + while (right - left + 1 - maxOccurred > k) { + char leftChr = s.charAt(left); + --cache[leftChr - 'A']; + left++; + } + result = Math.max(result, right - left + 1); + right++; + } + + return result; + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/LongestSpanWithSameSumArray.java b/src/main/java/practiceproblems/LongestSpanWithSameSumArray.java new file mode 100644 index 0000000..debe2b5 --- /dev/null +++ b/src/main/java/practiceproblems/LongestSpanWithSameSumArray.java @@ -0,0 +1,71 @@ +package practiceproblems; + +import java.util.HashMap; +import java.util.Map; + +/** + * Given two binary arrays arr1[] and arr2[] of same size n. + * Find length of the longest common span (i, j) + * where j >= i such that arr1[i] + arr1[i+1] + …. + arr1[j] = arr2[i] + arr2[i+1] + …. + arr2[j]. + *

+ * Expected time complexity is Θ(n). + *

+ * Input: arr1[] = {0, 1, 0, 0, 0, 0}; + * arr2[] = {1, 0, 1, 0, 0, 1}; + * Output: 4 + * The longest span with same sum is from index 1 to 4. + *

+ * Input: arr1[] = {0, 1, 0, 1, 1, 1, 1}; + * arr2[] = {1, 1, 1, 1, 1, 0, 1}; + * Output: 6 + * The longest span with same sum is from index 1 to 6. + */ +class LongestSpanWithSameSumArray { + // Returns largest common subarray with equal + // number of 0s and 1s + static int longestCommonSum(int[] arr1, int[] arr2, int n) { + // Find difference between the two + int[] arr = new int[n]; + // the reason we take the difference is, the resultant array + // will only contain 3 values 0,1,-1, checking for zero sum + // on the resultant array means we get the longest span where elements are same + for (int i = 0; i < n; i++) + arr[i] = arr1[i] - arr2[i]; + + // Creates an empty hashMap hM + Map hM = new HashMap<>(); + + int sum = 0; // Initialize sum of elements + int max_len = 0; // Initialize result + + // Traverse through the given array + for (int i = 0; i < n; i++) { + // Add current element to sum + sum += arr[i]; + + // To handle sum=0 at last index + if (sum == 0) + max_len = i + 1; + + // If this sum is seen before, + // then update max_len if required + if (hM.containsKey(sum)) + max_len = Math.max(max_len, i - hM.get(sum)); + + else // Else put this sum in hash table + hM.put(sum, i); + } + return max_len; + } + + + public static void main(String args[]) { + /* int[] arr1 = {0, 1, 0, 1, 1, 1, 1}; + int[] arr2 = {1, 1, 1, 1, 1, 0, 1};*/ + int arr1[] = {0, 1, 0, 0, 1, 1, 1, 0}; + int arr2[] = {1, 1, 1, 1, 1, 1, 0, 1}; + //{-1,0,-1,0,0,1,0} + int n = arr1.length; + System.out.println(longestCommonSum(arr1, arr2, n)); + } +} \ No newline at end of file diff --git a/src/geeksforgeeks/LongestSubArraySumUtmostK.java b/src/main/java/practiceproblems/LongestSubArraySumUtmostK.java similarity index 56% rename from src/geeksforgeeks/LongestSubArraySumUtmostK.java rename to src/main/java/practiceproblems/LongestSubArraySumUtmostK.java index 43e306e..867d142 100644 --- a/src/geeksforgeeks/LongestSubArraySumUtmostK.java +++ b/src/main/java/practiceproblems/LongestSubArraySumUtmostK.java @@ -1,25 +1,39 @@ -package geeksforgeeks; +package practiceproblems; /** + * revise + * * https://www.geeksforgeeks.org/longest-subarray-sum-elements-atmost-k/ + *

+ * Given an array of integers, + * our goal is to find the length of largest subarray + * having sum of its elements atmost ‘k’ where k>0. + *

+ * Examples: + *

+ * Input : arr[] = {1, 2, 1, 0, 1, 1, 0}, + * k = 4 + * Output : 5 + * Explanation: + * {1, 2, 1} => sum = 4, length = 3 + * {1, 2, 1, 0}, {2, 1, 0, 1} => sum = 4, length = 4 + * {1, 0, 1, 1, 0} =>5 sum = 3, length = 5 */ // array is non-negative class LongestSubArraySumUtmostK { - public static int atMostSum(int arr[], int n, int target) { + public static int utMostSum(int[] arr, int n, int target) { int sum = 0; int count = 0; int maxCount = 0; for (int i = 0; i < n; i++) { - //7 if ((sum + arr[i]) <= target) { sum += arr[i]; count++; } else if (sum != 0) { sum = sum - arr[i - count] + arr[i]; } - maxCount = Math.max(count, maxCount); } return maxCount; @@ -30,7 +44,6 @@ public static void main(String[] args) { int n = arr.length; int k = 4; - System.out.print(atMostSum(arr, n, k)); - + System.out.print(utMostSum(arr, n, k)); } } diff --git a/src/main/java/practiceproblems/LongestUniqueSubstring.java b/src/main/java/practiceproblems/LongestUniqueSubstring.java new file mode 100644 index 0000000..0a617e9 --- /dev/null +++ b/src/main/java/practiceproblems/LongestUniqueSubstring.java @@ -0,0 +1,58 @@ +package practiceproblems; + +import java.util.HashMap; +import java.util.Map; + +/** + * https://leetcode.com/problems/longest-substring-without-repeating-characters/ + */ + +public class LongestUniqueSubstring { + + public static int lengthOfLongestSubstring(String s) { + Map map = new HashMap<>(); + int begin = 0; + int end = 0; + int counter = 0; + int result = 0; + + while (end < s.length()) { + char c = s.charAt(end); + map.put(c, map.getOrDefault(c, 0) + 1); + if (map.get(c) > 1) { + counter++; + } + + while (counter > 0) { + char charTemp = s.charAt(begin); + if (map.get(charTemp) > 1) { + counter--; + } + map.put(charTemp, map.get(charTemp) - 1); + begin++; + } + result = Math.max(result, end - begin + 1); + end++; + } + return result; + } + + public static int lengthOfLongestSubstringOpt(String s) { + int res = 0, n = s.length(); + int[] arr = new int[256]; + int startIndex = 0; + for (int curr = 0; curr < n; curr++) { + // if already seen, pick the next element as start Index + startIndex = Math.max(startIndex, arr[s.charAt(curr)]); + + res = Math.max(res, curr - startIndex + 1); + // store curr+1=> next index, so that we can start from here + arr[s.charAt(curr)] = curr + 1; + } + return res; + } + + public static void main(String[] args) { + System.out.println(lengthOfLongestSubstringOpt("pwwkew")); + } +} \ No newline at end of file diff --git a/src/geeksforgeeks/MajorityVoting.java b/src/main/java/practiceproblems/MajorityVoting.java similarity index 58% rename from src/geeksforgeeks/MajorityVoting.java rename to src/main/java/practiceproblems/MajorityVoting.java index d857bdb..21874af 100644 --- a/src/geeksforgeeks/MajorityVoting.java +++ b/src/main/java/practiceproblems/MajorityVoting.java @@ -1,4 +1,4 @@ -package geeksforgeeks; +package practiceproblems; import java.util.ArrayList; import java.util.List; @@ -7,24 +7,25 @@ public class MajorityVoting { public static List majorityElementII(int[] nums) { - if (nums == null || nums.length == 0) + if (nums == null || nums.length == 0) { return new ArrayList<>(); + } List result = new ArrayList<>(); - int number1 = nums[0]; - int number2 = nums[0]; + int candidate1 = nums[0]; + int candidate2 = nums[0]; int count1 = 0; int count2 = 0; int len = nums.length; - for (int i = 0; i < len; i++) { - if (nums[i] == number1) + for (int num : nums) { + if (num == candidate1) { count1++; - else if (nums[i] == number2) + } else if (num == candidate2) { count2++; - else if (count1 == 0) { - number1 = nums[i]; + } else if (count1 == 0) { + candidate1 = num; count1 = 1; } else if (count2 == 0) { - number2 = nums[i]; + candidate2 = num; count2 = 1; } else { count1--; @@ -33,44 +34,46 @@ else if (count1 == 0) { } count1 = 0; count2 = 0; - for (int i = 0; i < len; i++) { - if (nums[i] == number1) + for (int num : nums) { + if (num == candidate1) { count1++; - else if (nums[i] == number2) + } else if (num == candidate2) { count2++; + } + } + if (count1 > len / 3) { + result.add(candidate1); + } + if (count2 > len / 3) { + result.add(candidate2); } - if (count1 > len / 3) - result.add(number1); - if (count2 > len / 3) - result.add(number2); return result; } public static int majorityElementI(int[] nums) { int count = 0; - int candidate = 0; - int majority = nums.length / 2; + int majorElem = 0; - for (int num : nums) { + for (int i : nums) { if (count == 0) { - candidate = num; + majorElem = i; } - count += (num == candidate) ? 1 : -1; - } - count = 0; - for (int num : nums) { - if (num == candidate) { + if (i == majorElem) { count++; + } else { + count--; } + } - return count > majority ? candidate : 0; + return majorElem; } public static void main(String[] args) { int[] arr = {1, 1, 1, 1, 1, 2, 2, 4, 4, 4, 4, 4, 2, 2}; System.out.println(majorityElementI(arr)); System.out.println(majorityElementII(arr)); + } } diff --git a/src/geeksforgeeks/MakeAnArrayPalindrome.java b/src/main/java/practiceproblems/MakeAnArrayPalindrome.java similarity index 90% rename from src/geeksforgeeks/MakeAnArrayPalindrome.java rename to src/main/java/practiceproblems/MakeAnArrayPalindrome.java index 775cb12..bf7ba7b 100644 --- a/src/geeksforgeeks/MakeAnArrayPalindrome.java +++ b/src/main/java/practiceproblems/MakeAnArrayPalindrome.java @@ -1,6 +1,9 @@ -package geeksforgeeks; +package practiceproblems; + +import java.util.Arrays; /** + * tricky palindrome * https://www.geeksforgeeks.org/find-minimum-number-of-merge-operations-to-make-an-array-palindrome/ */ class MakeAnArrayPalindrome { @@ -29,7 +32,7 @@ else if (arr[i] > arr[j]) { ans++; } } - + System.out.println(Arrays.toString(arr)); return ans; } diff --git a/src/main/java/practiceproblems/MatrixRowWithMax1.java b/src/main/java/practiceproblems/MatrixRowWithMax1.java new file mode 100644 index 0000000..910d394 --- /dev/null +++ b/src/main/java/practiceproblems/MatrixRowWithMax1.java @@ -0,0 +1,49 @@ +package practiceproblems; + +import java.util.ArrayList; + +/** + * https://www.geeksforgeeks.org/find-the-row-with-maximum-number-1s/ + */ +public class MatrixRowWithMax1 { + + public int[] rowAndMaximumOnes(int[][] mat) { + + var result = new ArrayList(); + int maxSoFar=-1; + int m = mat.length; + int n = mat[0].length; + + for(int i=0;imaxSoFar){ + maxSoFar = onesCount; + result = new ArrayList(); + result.add(i); + }else if(onesCount==maxSoFar){ + result.add(i); + } + + } + + return result.stream().mapToInt(i->i).toArray(); + + } + + public int lowerBound(int[] arr, int x){ + int left=0; + int right=arr.length-1; + + while(left map = new HashMap<>(); + + // priority_queue 'pq' implemented as + // max heap + PriorityQueue pq = + new PriorityQueue<>(Collections.reverseOrder()); + + // storing frequency of each element in map + for (int i = 0; i < n; i++) { + map.put(arr[i], map.getOrDefault(arr[i],0)+1); + } + + // inserting frequency of each element in 'pq' + for (Map.Entry entry : map.entrySet()) { + pq.add(entry.getValue()); + } + + while (k > 0) { + // get the top element of 'pq' + int temp = pq.poll(); + + // decrement the popped element by 1 + temp--; + + // if true, then push the element in 'pq' + if (temp > 0) + pq.add(temp); + k--; + } + + // Count all those elements that appear + // once after above operations. + int res = 0; + while (!pq.isEmpty()) { + pq.poll(); + res++; + } + + return res; + } + + /** + * Given an array of integers arr and an integer k. Find the least number of unique integers after removing exactly k elements. + * Input: arr = [5,5,4], k = 1 + * Output: 1 + * Explanation: Remove the single 4, only 5 is left. + * + * Input: arr = [4,3,1,1,3,3,2], k = 3 + * Output: 2 + * Explanation: Remove 4, 2 and either one of the two 1s or three 3s. 1 and 3 will be left. + * @param arr + * @param k + * @return + */ + public int findLeastNumOfUniqueInts(int[] arr, int k) { + if(arr.length==0) return 0; + Map frequencyMap= new HashMap<>(); + + for(int i: arr){ + frequencyMap.put(i,frequencyMap.getOrDefault(i,0)+1); + } + + PriorityQueue maxQueue= new PriorityQueue<>(); + + for(Map.Entry entry: frequencyMap.entrySet()){ + maxQueue.offer(entry.getValue()); + } + + while(k-- >0){ + int temp= maxQueue.poll(); + temp-=1; + if(temp>0) maxQueue.offer(temp); + + } + + int result=0; + while(!maxQueue.isEmpty()){ + result++; + } + return result; + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/MaxPointsInLine.java b/src/main/java/practiceproblems/MaxPointsInLine.java new file mode 100644 index 0000000..18c7cf7 --- /dev/null +++ b/src/main/java/practiceproblems/MaxPointsInLine.java @@ -0,0 +1,118 @@ +package practiceproblems; + + + +import graph.leetcode.Pair; + +import java.util.HashMap; +import java.util.Map; + +/** + * https://leetcode.com/problems/max-points-on-a-line + * + * + * input: [[2,1],[3,2],[4,3],[5,4]] + * slope: ^ 1/1 1/1 1/1 Slope is dy/dx, e.g. (5-2)/(4-1)=3/3=1/1 + * | o GCD is used to map equal slopes to identical. + * | o result: number of identical slopes + starting point = 3+1=4 + * | o + * | o + * +-------------------------- + * + * input: [[2,1],[3,2],[6,3],[5,4]] + * slope: ^ 1/1 2/1 1/1 We keep track of slopes related to the + * | o current point [2,1] in map. + * | o result: number of identical slopes + starting point = 2+1=3 + * | o + * | o + * +-------------------------- + * + * input: [[2,2],[3,2],[5,2],[6,2]] + * slope: ^ 1/0 1/0 1/0 GCD returns dx for pair [dx, 0]. + * | Then dx/dx / 0/dx = 1/0 + * | result: 3 + 1 = 4 + * | o o o o + * | + * +-------------------------- + * + * input: [[3,1],[3,2],[3,3],[3,4]] + * slope: ^ 0/1 0/1 0/1 GCD retuns dy for [o,dy]. + * | o Then 0/dy / dy/dy = 0/1 + * | o result: 3 + 1 = 4 + * | o + * | o + * +-------------------------- + * + * input: [[2,1],[3,2],[2,1],[4,1],[5,4],[2,1],[2,1]] + * slope: ^ 1/1 dup 1/0 1/1 dup dup + * | o Identical points have no slope. + * | We have 2 possible lines: 2 * 1/1 and 1 * 1/0. + * | o Keep track of duplicates and add them to max line. + * | 0 o result: 3 dups + max(2,1) + 1 = 6 + * +-------------------------- + * + * input: [[1,1],[5,1],[2,2],[3,3],[4,4],[6,2],[7,3]] + * slope: ^ 1/0 1/1 1/1 1/1 5/1 3/1 Slopes: max(1,3,1,1) = 3 + * slope: ^ -3/1 1/-1 1/-3 1/1 1/1 Slopes: max(1,1,1,2) = 2 + * | o The map tracks slopes for the current point only. + * | o o So parallel lines do not sum up points. + * | o o + * | o o result: 0 dups + max(3,2) + 1 = 4 + * +-------------------------- + * + * */ +public class MaxPointsInLine { + + public int maxPoints(int[][] points) { + if (points.length == 1) return 1; + int result = 2; + Map, Integer> cache = new HashMap<>(); + + for (int i = 0; i < points.length; i++) { + int duplicate = 0; + int count = 0; + cache.clear(); + for (int j = i + 1; j < points.length; j++) { + + int dx = points[j][0] - points[i][0]; + int dy = points[j][1] - points[i][1]; + + if (dx == 0 && dy == 0) { + duplicate++; + continue; + } + /** + * we need the slope: dx/dy. normally we can check dx/dy=1 + * but float rounds up the end and produces slightly different results, + * so instead we keep both dx and dy as the key. + * to make them identical for the identical slope, use GCD: greatest common divisor + */ + + int gcd = gcd(dx, dy); + if (gcd != 0) { + dx /= gcd; + dy /= gcd; + } + + // dx and dy define the slope. + // we keep the map for the current point i, so the full key is point[i]+slope excludes parallel lines. + // vertical line: dx==0, horizontal line: dy==0. + // GCD will set vertical: dx=0, dy=1, horizontal: dx=1, dy=0 + Pair line = new Pair<>(dx, dy); + + cache.put(line, cache.getOrDefault(line, 0) + 1); + count = Math.max(count, cache.get(line)); + } + + result = Math.max(result, count + duplicate + 1); + + } + + return result; + } + + public int gcd(int a, int b) { + if (b == 0) return a; + return gcd(b, a % b); + } +} diff --git a/src/main/java/practiceproblems/MaxSumTwoNonOverlappingSubArray.java b/src/main/java/practiceproblems/MaxSumTwoNonOverlappingSubArray.java new file mode 100644 index 0000000..9f28a42 --- /dev/null +++ b/src/main/java/practiceproblems/MaxSumTwoNonOverlappingSubArray.java @@ -0,0 +1,109 @@ +package practiceproblems; + +/** + * https://leetcode.com/problems/maximum-sum-of-two-non-overlapping-subarrays + *

+ * Basically it can be broken it into 2 cases: L is always before M vs M is always before L. + * + * + * Really tricky problem but not too hard once you see the method + * here is how I see it lets say you have the following array + * [2 , 1 , 5 , 6 , 0 , 9 , 5 , 0 , 3 , 8] with L = 3, M = 2 + * now lets look for our answer when M comes before L we will need to iterate the array like this + * [2 , 1 , 5 , 6 , 0 , 9 , 5 , 0 , 3 ] + * [ M ][ L ] + * [ M ][ L ] + * [ M ][ L ] + * [ M ][ L ] + * [ M ][ L ] + * + * where we keep track of the MMax and at every iteration we sum up the mMax and the current L and keep track of the maxValue + * the reasoning behind this is that since the arrays cant intercept mMax will keep track of greatest value of M before our current L + * + * once we do it one way we now run the algorithm with M and L reversed + * + * + * i-M-L i-M i + * 1, 2, 3, 4, 5, 6, 7 + * |- - L - - -|- M- - | + * To get the lengths: + * L= (i-M)-(i-M-L): M= i-(i-M) + * + * + * */ +public class MaxSumTwoNonOverlappingSubArray { + + public static int maxSumTwoNoOverlapConcise(int[] A, int L, int M) { + int[] prefixSum = new int[A.length + 1]; + for (int i = 0; i < A.length; ++i) { + prefixSum[i + 1] = prefixSum[i] + A[i]; + } + /* + maxSum(prefixSum, L, M) ==> find maximum sum for L length before index i and add it with every M length sum right to it + maxSum(prefixSum, M, L) ==> find maximum sum for M length before index i and add it with every L length sum right to it + */ + return Math.max(maxSum(prefixSum, L, M), maxSum(prefixSum, M, L)); + } + + private static int maxSum(int[] p, int L, int M) { + int ans = 0; + for (int i = L + M, maxL = 0; i < p.length; ++i) { + maxL = Math.max(maxL, p[i - M] - p[i - M - L]); // update max of L-length sub-array. + ans = Math.max(ans, maxL + p[i] - p[i - M]); // update max of the sum of L-length & M-length sub-arrays. + } + return ans; + } + + public static int maxSumTwoNoOverlap(int[] A, int L, int M) { + // L and M could be at left or right + // so we need to calculate the both to get the max non-overlapping sum of entire array + return Math.max(calculate(A, L, M), calculate(A, M, L)); + } + + private static int calculate(int[] A, int L, int M) { + int sum = 0; + int len = A.length; + + // calculate the prefix sum from A[0] to A[i] + int[] prefixSum = new int[len]; + prefixSum[0] = A[0]; + for(int i = 1; i < len; i++) { + prefixSum[i] = prefixSum[i-1] + A[i]; + } + + // calculate the maximum sum with length L with rightmost position at A[i], A[i] doesn't have to be included + int[] leftSum = new int[len]; + leftSum[L-1] = prefixSum[L-1]; + for(int i = L; i < len; i++) { + leftSum[i] = Math.max(leftSum[i-1], prefixSum[i] - prefixSum[i-L]); + } + + // calculate the suffix sum from A[i] to A[len-1] + int[] suffixSum = new int[len]; + suffixSum[len-1] = A[len-1]; + for(int i = len-2; i >= 0; i--) { + suffixSum[i] = suffixSum[i+1] + A[i]; + } + + // calculate the maximum sum with length M with leftmost position at A[i], A[i] doesn't have to be included + int[] rightSum = new int[len]; + rightSum[len-M] = suffixSum[len-M]; + for(int i = len-M-1; i >= 0; i--) { + rightSum[i] = Math.max(rightSum[i+1], suffixSum[i] - suffixSum[i+M]); + } + + // now we have all the data for max sum with length L from the left + // and max sum with length M from the right + // just iterate and add them up to find the max non-overlapping sum + // note the i+1 index is for non-overlapping + int res = Integer.MIN_VALUE; + for(int i = L-1; i <= len-M-1; i++) { + res = Math.max(leftSum[i] + rightSum[i+1], res); + } + + return res; + } + public static void main(String[] args) { + System.out.println(maxSumTwoNoOverlap(new int[]{0, 6, 5, 2, 2, 5, 1, 9, 4}, 1, 2)); + } +} diff --git a/src/main/java/practiceproblems/MaximumDifference.java b/src/main/java/practiceproblems/MaximumDifference.java new file mode 100644 index 0000000..1e3c436 --- /dev/null +++ b/src/main/java/practiceproblems/MaximumDifference.java @@ -0,0 +1,26 @@ +package practiceproblems; + +/** + * https://www.geeksforgeeks.org/maximum-difference-between-two-elements/ + */ +class MaximumDifference { + + static int maxDiff(int arr[], int arr_size) { + int maxDiff = 0; + int minElement = arr[0]; + for (int i = 1; i < arr_size; i++) { + if (arr[i] - minElement > maxDiff) { + maxDiff = arr[i] - minElement; + } + if (arr[i] < minElement) { + minElement = arr[i]; + } + } + return maxDiff; + } + + public static void main(String[] args) { + int arr[] = { 2, 4, 1, 3, 10, 8, 5 }; + System.out.println("Maximum difference is " + maxDiff(arr, arr.length)); + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/MaximumGap.java b/src/main/java/practiceproblems/MaximumGap.java new file mode 100644 index 0000000..ef75081 --- /dev/null +++ b/src/main/java/practiceproblems/MaximumGap.java @@ -0,0 +1,69 @@ +package practiceproblems; + +/** + * TODO + * Given an unsorted array, find the maximum difference between the successive elements in its sorted form. + * Return 0 if the array contains less than 2 elements. + * Input: [3,6,9,1] + * Output: 3 + * Explanation: The sorted form of the array is [1,3,6,9], either + * (3,6) or (6,9) has the maximum difference 3. + *

+ *

+ * trick is to do in O(N)= > Radix sort + */ +public class MaximumGap { + // The first step is to find the maximum value in nums array, it will + // be the threshold to end while loop. + // Then use the radix sort algorithm to sort based on each digit from Least Significant Bit + // (LSB) to Most Significant Bit (MSB), that's exactly what's showing + // in the link. + // (nums[i] / exp) % 10 is used to get the digit, for each digit, basically the digit itself serves as the index to + // access the count array. Count array stores the index to access aux + // array which stores the numbers after sorting based on the current + // digit. + // Finally, find the maximum gap from sorted array. + // Time and space complexities are both O(n). (Actually time is O(10n) at worst case for Integer.MAX_VALUE 2147483647) + public int maximumGap(int[] nums) { + if (nums == null || nums.length < 2) { + return 0; + } + + // m is the maximal number in nums + int m = nums[0]; + for (int i = 1; i < nums.length; i++) { + m = Math.max(m, nums[i]); + } + + int exp = 1; // 1, 10, 100, 1000 ... + int R = 10; // 10 digits + + int[] aux = new int[nums.length]; + + while (m / exp > 0) { // Go through all digits from LSB to MSB + int[] count = new int[R]; + + for (int num : nums) { + count[(num / exp) % 10]++; + } + + for (int i = 1; i < count.length; i++) { + count[i] += count[i - 1]; + } + + for (int i = nums.length - 1; i >= 0; i--) { + aux[--count[(nums[i] / exp) % 10]] = nums[i]; + } + + System.arraycopy(aux, 0, nums, 0, nums.length); + exp *= 10; + } + + int max = 0; + for (int i = 1; i < aux.length; i++) { + max = Math.max(max, aux[i] - aux[i - 1]); + } + + return max; + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/MaximumProductSubarray.java b/src/main/java/practiceproblems/MaximumProductSubarray.java new file mode 100644 index 0000000..b157ad0 --- /dev/null +++ b/src/main/java/practiceproblems/MaximumProductSubarray.java @@ -0,0 +1,145 @@ +package practiceproblems; + +/** + * https://www.geeksforgeeks.org/maximum-product-subarray/ + */ +public class MaximumProductSubarray { + + // 1, -2, -3, 0, 8, 7, -2 + public static int maxProductSubArray(int[] A) { + + if (A.length == 0) { + return 0; + } + + int maxHerePre = A[0]; + int minHerePre = A[0]; + int maxsofar = A[0]; + + for (int i = 1; i < A.length; i++) { + /** + * maxHere is updated by taking the maximum value among: + * + * Current number: + * This value will be picked if the accumulated product has been awful (even compared to the current number). + * This can happen when the current number has a preceding zero (e.g. [0,4]) or is preceded by a single negative number (e.g. [-3,5]). + * + * Product of last max_so_far and current number: + * This value will be picked if the accumulated product has been steadily increasing (all positive numbers). + * + * Product of last min_so_far and current number: + * This value will be picked if the current number is a negative number + * and the combo chain has been disrupted by a single negative number before + * (In a sense, this value is like an antidote to an already poisoned combo chain). + */ + int maxHere = Math.max(Math.max(maxHerePre * A[i], minHerePre * A[i]), A[i]); + int minHere = Math.min(Math.min(maxHerePre * A[i], minHerePre * A[i]), A[i]); + maxsofar = Math.max(maxHere, maxsofar); + maxHerePre = maxHere; + minHerePre = minHere; + } + return maxsofar; + } + + public static MaxSubarray getMaxSubarray(int[] inputArr) { + MaxSubarray result; + int start = 0; + int end = 0; + int maxSum = Integer.MIN_VALUE; + int currSum = 0; + int actualStart = 0; + for (int i = 0; i < inputArr.length; i++) { + + currSum += inputArr[i]; + + if (currSum > maxSum) { + maxSum = currSum; + actualStart = start; + end = i; + } + + if (currSum < 0) { + currSum = 0; + start = i + 1; + } + } + if (start > end) { + start = end; + } + + result = new MaxSubarray(maxSum, actualStart, end); + return result; + + } + + public static int maxSubArray(int[] nums) { + if (nums == null || nums.length == 0) return 0; + + int maxHere = 0; + int max = Integer.MIN_VALUE; + for (int i : nums) { + maxHere = Math.max(i, maxHere + i); + max = Math.max(maxHere, max); + } + + return max; + } + + public static void main(String[] args) { + int arr[] = {1, -2, -3, 0, 8, 7, -2}; + System.out.println("Maximum Sub array sum is " + maxSubArray(arr)); + System.out.println("Maximum Sub array product is " + maxProductSubArray(arr)); + } + + /** + * Simplest case. + * Consider this array [1,2,3,-4,5,6]. + * We can think of -4 as dividing the array into 2 halves, [1,2,3] and [5,6]. + * The forward traversal yields the max as 6, while the reverse traversal yields 30. + *

+ * Say the array has even number of negative numbers eg. [1,2,-3,-4,5,6]. + * Both forward and reverse traversals yield the same result, so it doesnt matter. + *

+ * Say the array has multiple odd number of negative integers. eg. [1,2,-3,-4,-5, 6]. + * We can think of the "last" negative number in each traversal breaks the array to 2 halves. + * In this case , the max array in forward traversal is the maximum of ([1,2,-3,-4] and [6]) which is 24. + * In the reverse, the split is delimited by -3. So the max subarrray is teh maximum of ([6,-5,-4] and [2]) + *

+ * Hence, in the end, its all about the presence of odd or even number of negative integers. + * In case of even, the product is always positive. In case of odd, the max product is limited by the last negative integer in each traversal. + * + * @param nums + * @return + */ + public int maxProduct(int[] nums) { + + int n = nums.length; + double left = 1; + double right = 1; + double ans = nums[0]; + for (int i = 0; i < n; i++) { + left = left == 0 ? 1 : left; + right = right == 0 ? 1 : right; + + left *= nums[i]; + right *= nums[n - i - 1]; + ans = Math.max(ans, Math.max(left, right)); + } + return (int) ans; + + } + + static class MaxSubarray { + int maxSum; + int startIndex; + int endIndex; + + public MaxSubarray(int maxSum, int startIndex, int endIndex) { + this.maxSum = maxSum; + this.startIndex = startIndex; + this.endIndex = endIndex; + } + } +} + + diff --git a/src/main/java/practiceproblems/MaximumSubstringWithKDistinctChar.java b/src/main/java/practiceproblems/MaximumSubstringWithKDistinctChar.java new file mode 100644 index 0000000..a633f7d --- /dev/null +++ b/src/main/java/practiceproblems/MaximumSubstringWithKDistinctChar.java @@ -0,0 +1,85 @@ +package practiceproblems; + +import java.util.HashMap; +import java.util.Map; + +/** + * https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/ + *

+ * https://www.lintcode.com/problem/longest-substring-with-at-most-two-distinct-characters/description + */ +public class MaximumSubstringWithKDistinctChar { + + public static void main(String[] args) { + System.out.println(lengthOfLongestSubstringTwoDistinct("aaaaaaaa", 2)); + System.out.println(""); + // System.out.println(lengthOfLongestSubstringKDistinct("eqgkcwGFvjjmxutystqdfhuMblWbylgjxsxgnoh", 2)); + } + + public static int lengthOfLongestSubstringTwoDistinct(String s, int k) { + Map map = new HashMap<>(); + int start = 0; + int end = 0; + int counter = 0; + int length = 0; + int startIndex=0; + while (end < s.length()) { + char c = s.charAt(end); + map.put(c, map.getOrDefault(c, 0) + 1); + if (map.get(c) == 1) { + counter++;//new char + } + end++; + while (counter > k) { + char cTemp = s.charAt(start); + map.put(cTemp, map.get(cTemp) - 1); + if (map.get(cTemp) == 0) { + counter--; + } + start++; + } + if(end - start>length){ + startIndex=start; + length= end-start; + } + + } + + System.out.println(s.substring(startIndex,startIndex+length)); + return length; + } + + // improvised solution + public static int lengthOfLongestSubstringKDistinct(String s, int k) { + if (s == null || s.isEmpty()) { + return 0; + } + if (k == 0) { + return 0; + } + int start = 0; + int maxCount = 0; + int[] arr = new int[26]; + s = s.toLowerCase(); + for (int i = 0; i < s.length(); i++) { + + if (arr[s.charAt(i) - 'a'] == 0) { + k--; + } + + arr[s.charAt(i) - 'a']++; + while (k == -1) { + + arr[s.charAt(start) - 'a']--; + if (arr[s.charAt(start) - 'a'] == 0) { + k++; + } + start++; + } + maxCount = Math.max(maxCount, i - start); + } + return maxCount + 1; + } + + +} diff --git a/src/main/java/practiceproblems/MaximumUnsortedSubarray.java b/src/main/java/practiceproblems/MaximumUnsortedSubarray.java new file mode 100644 index 0000000..3767d0b --- /dev/null +++ b/src/main/java/practiceproblems/MaximumUnsortedSubarray.java @@ -0,0 +1,63 @@ +package practiceproblems; + +//https://leetcode.com/problems/shortest-unsorted-continuous-subarray/ + +/** + * tricky array traversal + * + * Given an integer array, you need to find one continuous subarray that if you only sort this subarray in ascending order, + * then the whole array will be sorted in ascending order, too. + *

+ * You need to find the shortest such subarray and output its length. + *

+ * Example 1: + * Input: [2, 6, 4, 8, 10, 9, 15] + * Output: 5 + * Explanation: You need to sort [6, 4, 8, 10, 9] in ascending order to make the whole array sorted in ascending order. + */ +public class MaximumUnsortedSubarray { + + public static int findUnsortedSubarray(int[] nums) { + if (nums == null) { + return 0; + } + if (nums.length == 0 || nums.length == 1) { + return 0; + } + + + int min = Integer.MAX_VALUE; + int begin = -1; + //iterate from end of array + //find the last element which is bigger than the last seen min from + //its right side and mark it as begin + for (int i = nums.length - 1; i >= 0; i--) { + min = Math.min(min, nums[i]); + if (nums[i] > min) { + begin = i; + } + } + if (begin == -1) return 0; + int max = Integer.MIN_VALUE; + int end = -2; + //iterate from beginning of array + //find the last element which is smaller than the last seen max from + //its left side and mark it as end + for (int i = 0; i < nums.length; i++) { + max = Math.max(max, nums[i]); + if (nums[i] < max) { + end = i; + } + } + return end - begin + 1; + } + + public static void main(final String[] args) { + //1, 1, 10, 10, 15, 10, 15, 10,10, 15, 10, 15 + //1, 3, 2, 4, 5 + //4, 15, 4, 4, 15, 18, 20 + //2, 6, 1, 8, 10, 9, 15 + findUnsortedSubarray(new int[]{4, 15, 4, 4, 15, 18, 20}); + + } +} diff --git a/src/main/java/practiceproblems/MedianOfKWindow.java b/src/main/java/practiceproblems/MedianOfKWindow.java new file mode 100644 index 0000000..87c2023 --- /dev/null +++ b/src/main/java/practiceproblems/MedianOfKWindow.java @@ -0,0 +1,51 @@ +package practiceproblems; + +import java.util.Collections; +import java.util.PriorityQueue; + +public class MedianOfKWindow { + public double[] medianSlidingWindow(int[] nums, int k) { + MedianQueue medianHeap = new MedianQueue(); + + double[] result = new double[nums.length - k + 1]; + + int resultIndex = 0; + + for (int i = 0; i < nums.length; i++) { + medianHeap.offer(nums[i]); + if (medianHeap.size() == k) { + result[resultIndex++] = medianHeap.median(); + medianHeap.remove(nums[i + 1 - k]); + } + + } + + return result; + } + + static class MedianQueue { + PriorityQueue minQueue = new PriorityQueue<>(); + PriorityQueue maxQueue = new PriorityQueue<>(Collections.reverseOrder()); + + public void offer(int x) { + maxQueue.offer(x); + minQueue.offer(maxQueue.poll()); + if (maxQueue.size() < minQueue.size()) { + maxQueue.offer(minQueue.poll()); + } + + } + + public double median() { + return maxQueue.size() > minQueue.size() ? maxQueue.peek() : ((long) maxQueue.peek() + minQueue.peek()) * 0.5; + } + + public int size() { + return minQueue.size() + maxQueue.size(); + } + + public boolean remove(int x) { + return minQueue.remove(x) || maxQueue.remove(x); + } + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/MedianOfRunningIntegers.java b/src/main/java/practiceproblems/MedianOfRunningIntegers.java new file mode 100644 index 0000000..a1f8b06 --- /dev/null +++ b/src/main/java/practiceproblems/MedianOfRunningIntegers.java @@ -0,0 +1,60 @@ +package practiceproblems; + +import java.util.Collections; +import java.util.PriorityQueue; + +/** + * https://leetcode.com/problems/find-median-from-data-stream/ + */ +public class MedianOfRunningIntegers { + + PriorityQueue upperHalf = new PriorityQueue<>(); + PriorityQueue lowerHalf = new PriorityQueue<>(Collections.reverseOrder()); + + // 6,8,1,4,9,2,3,5 + // median is a middle element in sorted array + // in a sorted array if we choose a point the immediate left to that point is maxLeft (max of all left) + // the immediate right to that point is minRight (min of all right) + // to mimic that here the right(max) values are stored in min heap + // the left(min) values are stored in maxheap + // maxHeap.. i.. minHeap + // if odd return from maxHeap + public void addNum(int num) { + // Insert in lowerHalf if it's empty or + // if number being inserted is less than the peek of lowerHalf otherwise insert in upperHalf + if (lowerHalf.isEmpty() || num <= lowerHalf.peek()) { + lowerHalf.add(num); + } else { + upperHalf.add(num); + } + + // We also need to ensure that the halves are balanced i.e. + // there is no more than a difference of 1 in size of both halves + // Let lowerHalf be the one to hold one extra element if the size of total + // data stream is odd otherwise be equal to upperHalf + if (upperHalf.size() > lowerHalf.size()) { // If an element added above made upperHalf have one more element than lowerHalf then we poll it and put it into lowerHalf + lowerHalf.add(upperHalf.poll()); + } else if (lowerHalf.size() > upperHalf.size() + 1) { + // If an element added above, made lowerHalf have 2 more elements then upperHalf then we put one into upperHalf from lowerHalf + upperHalf.add(lowerHalf.poll()); + } + } + + public double findMedian() { + if (lowerHalf.size() == upperHalf.size()) { + return (lowerHalf.peek() + upperHalf.peek()) / 2.0; + } else { + return lowerHalf.peek(); + } + } + + public static void main(String[] args) { + MedianOfRunningIntegers median = new MedianOfRunningIntegers(); + int A[] = {5, 15, 1, 3, 2, 8, 7, 9, 10, 6, 11, 4}; + for (int num : A) { + median.addNum(num); + System.out.println(median.findMedian()); + } + } + +} diff --git a/src/main/java/practiceproblems/MinAdjSwapsToMakePalindrome.java b/src/main/java/practiceproblems/MinAdjSwapsToMakePalindrome.java new file mode 100644 index 0000000..a85b195 --- /dev/null +++ b/src/main/java/practiceproblems/MinAdjSwapsToMakePalindrome.java @@ -0,0 +1,60 @@ +package practiceproblems; + +/** + * https://leetcode.com/discuss/interview-question/351783/ + * https://www.youtube.com/watch?v=zXpYs8j5oI8&ab_channel=Insidecode + * + * TODO + */ +public class MinAdjSwapsToMakePalindrome { + + private int getNoOfSwaps(String s) { + if (s == null || s.length() == 0) return -1; + int totalSwaps = 0; + + if (isShuffledPalindrome(s)) { + char[] chars = s.toCharArray(); + int left = 0, right = chars.length - 1; + + while (right > left) { + if (chars[left] != chars[right]) { + int searcher = right; + while (searcher > left && chars[searcher] != chars[left]) searcher--; + + if (searcher == left) { //When no matching character found + swap(chars, left, left + 1); + totalSwaps++; + + } else { //When Matching character found swap until K reaches p2 position + while (searcher < right) { + swap(chars, searcher, searcher + 1); + totalSwaps++; + searcher++; + } + left++; + right--; + } + } else { + left++; + right--; //When the characters are equal move on + } + } + return totalSwaps; + } else return -1; + } + + private static void swap(char[] chars, int k, int i) { + char temp = chars[k]; + chars[k] = chars[i]; + chars[i] = temp; + } + + private boolean isShuffledPalindrome(String s) { + int[] occurrence = new int[26]; + int oddCount = 0; + + for (int i = 0; i < s.length(); i++) occurrence[s.charAt(i) - 'a']++; + for (int value : occurrence) if (value % 2 != 0) oddCount++; + return oddCount <= 1; + } +} diff --git a/src/main/java/practiceproblems/MinCostRopeConnect.java b/src/main/java/practiceproblems/MinCostRopeConnect.java new file mode 100644 index 0000000..77a63c5 --- /dev/null +++ b/src/main/java/practiceproblems/MinCostRopeConnect.java @@ -0,0 +1,34 @@ +package practiceproblems; + +import java.util.PriorityQueue; + +/** + * https://www.geeksforgeeks.org/connect-n-ropes-minimum-cost/ + */ +public class MinCostRopeConnect { + + // 6 + 4 = 10 .. 10 + 3 = 13 .. 13 +2 = 15 + + // 2+3 = 5 .. 5 + 4 = 9 .. 9 + 6 = 15 .. + + public static void main(String[] args) { + int arr[] = {4, 3, 2, 6}; + + MinCostRopeConnect rope = new MinCostRopeConnect(); + rope.connectRopes(arr); + } + + private void connectRopes(int[] arr) { + PriorityQueue pq = new PriorityQueue<>(); + for (int i : arr) + pq.add(i); + + while (pq.size() > 1) { + Integer remove = pq.remove(); + Integer remove2 = pq.remove(); + + System.out.println("cost" + (remove + remove2)); + pq.add(remove + remove2); + } + } +} diff --git a/src/main/java/practiceproblems/MinIncrementToMakeArrayUnique.java b/src/main/java/practiceproblems/MinIncrementToMakeArrayUnique.java new file mode 100644 index 0000000..97761d7 --- /dev/null +++ b/src/main/java/practiceproblems/MinIncrementToMakeArrayUnique.java @@ -0,0 +1,106 @@ +package practiceproblems; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +public class MinIncrementToMakeArrayUnique { + + /** + * O(N^2) + * + * @param A + * @return + */ + public int minIncrementForUniqueBruteForce(int[] A) { + if (A == null || A.length == 0) return 0; + Map map = new HashMap<>(); + for (int i : A) { + map.put(i, map.getOrDefault(i, 0) + 1); + } + int result = 0; + for (int i = 0; i < A.length; i++) { + if (map.get(A[i]) > 1) { + int temp = A[i]; + while (map.containsKey(temp)) { + temp++; + result++; + } + map.put(A[i], map.get(A[i]) - 1); + map.put(temp, map.getOrDefault(i, 0) + 1); + A[i] = temp; + } + } + //System.out.println(Arrays.toString(A)); + + return result; + } + + + /** + * tricky make array unique + * @param A + * @return + */ + public int minIncrementForUniqueSort(int[] A) { + Arrays.sort(A); + int count = 0; + /** + * in the case of [1,1,2,2,3,3]: + * A[1] is incremented to 2 (+1) + * [1,2,2,2,3,3] + * A[2] is incremented to 3 (+1) + * [1,2,3,2,3,3] + * A[3] is incremented to 4 (+2) + * [1,2,3,4,3,3] + * A[4] is incremented to 5, (+2) + * [1,2,3,4,5,3] + * A[5] is incremented to 6, (+3) + * [1,2,3,4,5,6] + */ + for (int i = 1; i < A.length; i++) { + //condition '<=' is because [1,2,2,2,3,7] when optimising this array + // when i=2 completes iteration the arr becomes [1,2,3,2,3,7] + // when i=3 we need to check if nums[i]<=nums[i-1] + if (A[i] <= A[i - 1]) { + int diff = A[i - 1] - A[i] + 1; + A[i] = A[i] + diff; + count += diff; + } + } + + return count; + } + + + /** + * tricky array processing + * O(N) + * + * @param arr + * @return + */ + public int minIncrementForUnique(int[] arr) { + if (arr == null || arr.length == 0) + return 0; + + int moves = 0; + int[] freq = new int[110000]; //given the max num = 40000 and array length = 39999, the worst case will fit in 80000 + for (int num : arr) + freq[num]++; + + for (int i = 0; i < freq.length - 1; i++) { + if (freq[i] <= 1) //no need to move anything! + continue; + //consider an example like where frequency of number 3 is 4 + //remaining that needs to be "reevaluated" is 3 (since one 3 is valid in this slot) + //if we were to add 1 to value 3, it is 4 + //since we have frequency of 3, its like now 4 has 3 frequency + //we repeat this process + int remain = freq[i] - 1; + moves += remain; + freq[i + 1] += remain; + } + return moves; + } +} diff --git a/src/main/java/practiceproblems/MinOperationToMakeArrayIncreasing.java b/src/main/java/practiceproblems/MinOperationToMakeArrayIncreasing.java new file mode 100644 index 0000000..ec8cb41 --- /dev/null +++ b/src/main/java/practiceproblems/MinOperationToMakeArrayIncreasing.java @@ -0,0 +1,26 @@ +package practiceproblems; + +/** + * https://leetcode.com/problems/minimum-operations-to-make-the-array-increasing/ + */ +public class MinOperationToMakeArrayIncreasing { + + public int minOperations(int[] nums) { + if (nums.length == 0 || nums.length == 1) return 0; + int total = 0; + + /** + If at any point, nums[i] <= nums[i - 1], + then we need to increment nums[i] to make the array strictly increasing. + The number of increments needed is given by - nums[i - 1] + nums[i] + 1. + Basically, it is the number of increments needed to take nums[i] to atleast nums[i - 1] + 1. + **/ + for (int i = 1; i < nums.length; i++) { + if (nums[i] > nums[i - 1]) continue; + total += Math.abs(nums[i] - nums[i - 1]) + 1; + nums[i] += Math.abs(nums[i] - nums[i - 1]) + 1; + } + + return total; + } +} diff --git a/src/main/java/practiceproblems/MinRefuelStops.java b/src/main/java/practiceproblems/MinRefuelStops.java new file mode 100644 index 0000000..2776be4 --- /dev/null +++ b/src/main/java/practiceproblems/MinRefuelStops.java @@ -0,0 +1,35 @@ +package practiceproblems; + +import java.util.PriorityQueue; + +/** + * tricky priority queue + * revise + */ +public class MinRefuelStops { + + public int minRefuelStops(int target, int startFuel, int[][] stations) { + if (startFuel >= target) return 0; + int curFarthest = startFuel, refuel = 0; + PriorityQueue pq = new PriorityQueue<>((a, b) -> b - a); + for (int[] station : stations) { + // check if we can reach next station + // if we cannot reach this station, refuel the gas from the previous station with most gas + // redo the operation until we get enough gas to reach this station + while (curFarthest < station[0]) { + if (pq.isEmpty()) + return -1; // if we refuel in each station but still cannot reach this station, return -1 + curFarthest += pq.poll(); + refuel++; + } + pq.offer(station[1]); + } + // now we have reached the last station, check if we can reach the target + while (curFarthest < target) { + if (pq.isEmpty()) return -1; + curFarthest += pq.poll(); + refuel++; + } + return refuel; + } +} diff --git a/src/main/java/practiceproblems/MinStepsToConvertXtoY.java b/src/main/java/practiceproblems/MinStepsToConvertXtoY.java new file mode 100644 index 0000000..2fc12d7 --- /dev/null +++ b/src/main/java/practiceproblems/MinStepsToConvertXtoY.java @@ -0,0 +1,49 @@ +package practiceproblems; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Set; + +class MinStepsToConvertXtoY { + private static class GFG{ + int val; + int steps; + GFG(int source, int steps){ + this.val=source; + this.steps=steps; + } + } + private static int minOperations(int src, int target) { + + Set visited = new HashSet<>(1000); + LinkedList queue = new LinkedList(); + + GFG node = new GFG(src, 0); + + queue.offer(node); + visited.add(node); + + while (!queue.isEmpty()) { + GFG temp = queue.poll(); + visited.add(temp); + + if (temp.val == target) { + return temp.steps; + } + + int mul = temp.val * 2; + int sub = temp.val - 1; + + // given constraints + if (mul > 0 && mul < 1000) { + GFG nodeMul = new GFG(mul, temp.steps + 1); + queue.offer(nodeMul); + } + if (sub > 0 && sub < 1000) { + GFG nodeSub = new GFG(sub, temp.steps + 1); + queue.offer(nodeSub); + } + } + return -1; + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/MinTimeRotOranges.java b/src/main/java/practiceproblems/MinTimeRotOranges.java new file mode 100644 index 0000000..39e22ac --- /dev/null +++ b/src/main/java/practiceproblems/MinTimeRotOranges.java @@ -0,0 +1,61 @@ +package practiceproblems; + +import java.util.ArrayDeque; +import java.util.Deque; + +/** + * https://github.com/bibhas-abhishek/projects/tree/master/MinTimeRotOranges + * https://www.geeksforgeeks.org/minimum-time-required-so-that-all-oranges-become-rotten/ + */ + +public class MinTimeRotOranges { + + public int orangesRotting(int[][] grid) { + if (grid == null || grid.length == 0) return 0; + int countFresh = 0; + int[][] dirs = new int[][]{{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; + Deque queue = new ArrayDeque<>(); + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[0].length; j++) { + if (grid[i][j] == 2) { + queue.offer(new int[]{i, j}); + } else if (grid[i][j] == 1) { + countFresh++; + } + } + } + if (countFresh == 0) return 0; + int result = -1; + while (!queue.isEmpty()) { + int size = queue.size(); + + for (int i = 0; i < size; i++) { + int[] temp = queue.poll(); + + for (int[] dir : dirs) { + int newX = temp[0] + dir[0]; + int newY = temp[1] + dir[1]; + + if (newX < 0 || newX >= grid.length || newY < 0 || newY >= grid[0].length || grid[newX][newY] == 0 || grid[newX][newY] == 2) { + continue; + } + + queue.offer(new int[]{newX, newY}); + grid[newX][newY] = 2; + countFresh--; + } + } + + result++; + } + + + return countFresh != 0 ? -1 : result; + } + + public static void main(String[] args) { + int grid[][] = {{2, 1, 0, 1, 1}, {1, 0, 2, 1, 1}, {1, 1, 1, 1, 1}}; + System.out.println(new MinTimeRotOranges().orangesRotting(grid)); + } + +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/MinimumBribes.java b/src/main/java/practiceproblems/MinimumBribes.java new file mode 100644 index 0000000..647cbf8 --- /dev/null +++ b/src/main/java/practiceproblems/MinimumBribes.java @@ -0,0 +1,52 @@ +package practiceproblems; + +/** + * TODO + * + * https://www.hackerrank.com/challenges/new-year-chaos/problem + * https://www.youtube.com/watch?v=UpmVTEvaXPE + * Any person in the queue can bribe the person directly in front of them to swap positions. + * If two people swap positions, they still wear the same sticker denoting their original place in line. + * One person can bribe at most two other persons. + * That is to say, if n = 8, and Person 5 bribes Person 4, the queue will look like this: 1,2,3,5,4,6,7,8. + * Fascinated by this chaotic queue, you decide you must know the minimum number of bribes that took place to get the queue into its current state! + * No person can bribe more than 2 persons if yes then print chaotic + * Input: 2 1 5 3 4 + * Output: 3 + *

+ * Input: 2 5 1 3 4 + * Output: Too chaotic + */ +public class MinimumBribes { + // input is 2 1 5 3 4, + // when index is at 2(val=>5) we see how many elements lesser than 5 is there + // if the value is greater that 2 we print too chaotic + // else we add it to result + void minimumBribes(int[] q) { + int bribes = 0; + for (int i = q.length - 1; i >= 0; i--) { + if (q[i] == i + 1) continue; + if (i - 1 >= 0 && q[i - 1] == i + 1) { + q[i - 1] = q[i]; + q[i] = i + 1; + bribes += 1; + } else if (i - 2 >= 0 && q[i - 2] == i + 2) { + //2,1,5,3,4 we need to swap 2 elements to restore the array + q[i - 2] = q[i - 1]; + q[i - 1] = q[i]; + q[i] = i + 1; + bribes += 2; + } else { + System.out.println("Too Chaotic"); + return; + } + } + System.out.println(bribes); + } + + public static void main(String[] args) { + // inputs 5,1,2,3,7,8,6,4 => too chaotic + // 1,2,5,3,7,8,6,4 => 7 + new MinimumBribes().minimumBribes(new int[]{2, 1, 5, 3, 4}); + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/MinimumDistanceBetweenTwoNumbers.java b/src/main/java/practiceproblems/MinimumDistanceBetweenTwoNumbers.java new file mode 100644 index 0000000..d203eb0 --- /dev/null +++ b/src/main/java/practiceproblems/MinimumDistanceBetweenTwoNumbers.java @@ -0,0 +1,52 @@ +package practiceproblems; + + +/* +https://www.geeksforgeeks.org/find-the-minimum-distance-between-two-numbers/ + +Given an unsorted array arr[] and two numbers x and y, find the minimum distance between x and y in arr[]. +The array might also contain duplicates. +You may assume that both x and y are different and present in arr[]. + +Input: arr[] = {2, 5, 3, 5, 4, 4, 2, 3}, +x = 3, y = 2 +Output: Minimum distance between 3 +and 2 is 1. +Explanation:3 is at index 7 and 2 is at +index 6, so the distance is 1 +*/ + + +class MinimumDistanceBetweenTwoNumbers { + + int minDist(int arr[], int n, int x, int y) { + Integer xIndex = null; + Integer yIndex = null; + int minDist = Integer.MAX_VALUE; + + for (int i = 0; i < n; i++) { + if (arr[i] == x) { + xIndex = i; + } + if (arr[i] == y) { + yIndex = i; + } + + if (xIndex != null && yIndex != null) { + minDist = Math.min(minDist, Math.abs(xIndex - yIndex)); + } + } + return minDist == Integer.MAX_VALUE ? -1 : minDist; + } + + public static void main(String[] args) { + MinimumDistanceBetweenTwoNumbers min = new MinimumDistanceBetweenTwoNumbers(); + int arr[] = {3, 5, 4, 2, 6, 5, 6, 6, 5, 4, 8, 3}; + int arr1[] = {3, 5, 4, 3, 1, 2, 4, 6, 5, 6, 6, 5, 4, 8, 3}; + int n = arr.length; + int x = 3; + int y = 6; + + System.out.println("Minimum distance between " + x + " and " + y + " is " + min.minDist(arr, n, x, y)); + } +} diff --git a/src/main/java/practiceproblems/MinimumIndexDistanceOfMaximumNumbers.java b/src/main/java/practiceproblems/MinimumIndexDistanceOfMaximumNumbers.java new file mode 100644 index 0000000..1c9ef2e --- /dev/null +++ b/src/main/java/practiceproblems/MinimumIndexDistanceOfMaximumNumbers.java @@ -0,0 +1,31 @@ +package practiceproblems; + +/** + * https://www.geeksforgeeks.org/minimum-distance-between-two-occurrences-of-maximum/ + */ +class MinimumIndexDistanceOfMaximumNumbers { + + static int minDistance(int arr[], int n) { + int maximumElement = arr[0]; + int minDist = n; + int index = 0; + + for (int i = 1; i < n; i++) { + if (maximumElement == arr[i]) { + minDist = Math.min(minDist, (i - index)); + index = i; + } else if (maximumElement < arr[i]) { + maximumElement = arr[i]; + minDist = n; + index = i; + } + } + return minDist - 1; + } + + public static void main(String[] args) { + int arr[] = { 6, 3, 1, 3, 6, 5, 4, 1 }; + int n = arr.length; + System.out.print("Minimum distance = " + minDistance(arr, n)); + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/MinimumStepsKnight.java b/src/main/java/practiceproblems/MinimumStepsKnight.java new file mode 100644 index 0000000..f2e1505 --- /dev/null +++ b/src/main/java/practiceproblems/MinimumStepsKnight.java @@ -0,0 +1,126 @@ +package practiceproblems; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Queue; +import java.util.Set; + +/** + * https://www.geeksforgeeks.org/minimum-steps-reach-target-knight/ + *

+ * https://www.techiedelight.com/chess-knight-problem-find-shortest-path-source-destination/ + */ +class MinimumStepsKnight { + + static class Cell { + int x; + int y; + int steps; + + public Cell(int x, int y, int steps) { + this.x = x; + this.y = y; + this.steps = steps; + } + + } + + // Utility method returns true if (x, y) lies + // inside Board + static boolean isInside(int x, int y, int N) { + if (x >= 1 && x <= N && y >= 1 && y <= N) { + return true; + } + return false; + } + + // Method returns minimum step + // to reach target position + static int minStepToReachTarget(int knightPos[], int targetPos[], int N) { + // x and y direction, where a knight can move + int dx[] = {-2, -1, 1, 2, -2, -1, 1, 2}; + int dy[] = {-1, -2, -2, -1, 1, 2, 2, 1}; + + // queue for storing states of knight in board + Queue q = new LinkedList<>(); + + // push starting position of knight with 0 distance + q.add(new Cell(knightPos[0], knightPos[1], 0)); + + Cell cell; + int x, y; + boolean visit[][] = new boolean[N + 1][N + 1]; + + // make all cell unvisited + for (int i = 1; i <= N; i++) + for (int j = 1; j <= N; j++) + visit[i][j] = false; + + // visit starting state + visit[knightPos[0]][knightPos[1]] = true; + + // loop untill we have one element in queue + while (!q.isEmpty()) { + cell = q.remove(); + + // if current cell is equal to target cell, + // return its distance + if (cell.x == targetPos[0] && cell.y == targetPos[1]) { + return cell.steps; + } + + // loop for all reachable states + for (int i = 0; i < 8; i++) { + x = cell.x + dx[i]; + y = cell.y + dy[i]; + + // If reachable state is not yet visited and + // inside board, push that state into queue + if (isInside(x, y, N) && !visit[x][y]) { + visit[x][y] = true; + q.add(new Cell(x, y, cell.steps + 1)); + } + } + } + return Integer.MAX_VALUE; + } + + public int minKnightMoves(int x, int y) { + x = Math.abs(x); + y = Math.abs(y); // If we can reach x,y in one quadrant then we can do it for all others in the same number of moves too. + if (x == 1 && y == 1) + return 2; // Special case for 1,1 because there is a more efficient way to reach it if we allow negative positions + int[][] dirs = new int[][]{{-2, -1}, {-2, 1}, {-1, 2}, {1, 2}, {2, 1}, {2, -1}, {1, -2}, {-1, -2}}; + Deque queue = new ArrayDeque<>(); + Set seen = new HashSet<>(); + queue.addLast(new int[]{0, 0}); + seen.add("0,0"); + int result = 0; + while (!queue.isEmpty()) { + int size = queue.size(); // Number of unique positions reachable with exactly result moves. + for (int i = 0; i < size; ++i) { + int[] currPos = queue.removeFirst(); + if (currPos[0] == x && currPos[1] == y) return result; + for (int[] currDir : dirs) { + int newX = currPos[0] + currDir[0]; + int newY = currPos[1] + currDir[1]; + if (newX >= 0 && newY >= 0 && !seen.contains(newX + "," + newY)) { + seen.add(newX + "," + newY); + queue.addLast(new int[]{newX, newY}); + } + } + } + result++; + } + return -1; // Can't be reached on an infinite chess board. + } + + public static void main(String[] args) { + int N = 30; + int[] knightPos = {1, 1}; + int[] targetPos = {30, 30}; + System.out.println(minStepToReachTarget(knightPos, targetPos, N)); + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/MinimumSubArrayLength.java b/src/main/java/practiceproblems/MinimumSubArrayLength.java new file mode 100644 index 0000000..dac78f2 --- /dev/null +++ b/src/main/java/practiceproblems/MinimumSubArrayLength.java @@ -0,0 +1,31 @@ +package practiceproblems; + +public class MinimumSubArrayLength { + + /** + * tricky int sliding window + */ + public static int minSubArrayLen(int target, int[] nums) { + + int left = 0; + int right = 0; + int sum = 0; + int result = Integer.MAX_VALUE; + while (left < nums.length) { + sum += nums[left]; + while (sum >= target) { + result = Math.min(result, left - right + 1); + sum -= nums[right]; + right++; + } + + left++; + } + + return result == Integer.MAX_VALUE ? 0 : result; + } + + public static void main(String[] args) { + minSubArrayLen(7, new int[]{2, 3, 1, 2, 4, 3}); + } +} diff --git a/src/main/java/practiceproblems/MinimumSwapSortArray.java b/src/main/java/practiceproblems/MinimumSwapSortArray.java new file mode 100644 index 0000000..4c7ee92 --- /dev/null +++ b/src/main/java/practiceproblems/MinimumSwapSortArray.java @@ -0,0 +1,110 @@ +package practiceproblems; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; + + +/** + * https://www.geeksforgeeks.org/minimum-number-swaps-required-sort-array/ + */ +public class MinimumSwapSortArray { + // Return the minimum number + // of swaps required to sort the array + public static int minSwaps(int[] nums) { + int len = nums.length; + HashMap map = new HashMap<>(); + for (int i = 0; i < len; i++) + map.put(nums[i], i); + + Arrays.sort(nums); + + // To keep track of visited elements. Initialize + // all elements as not visited or false. + boolean[] visited = new boolean[len]; + + // Initialize result + int ans = 0; + List> results = new ArrayList<>(); + for (int i = 0; i < len; i++) { + List temp = new ArrayList<>(); + // already swapped and corrected or + // already present at correct pos + if (visited[i] || map.get(nums[i]) == i) + continue; + + int j = i, cycle_size = 0; + while (!visited[j]) { + visited[j] = true; + + // move to next node + j = map.get(nums[j]); // nums is sorted now remember + cycle_size++; + temp.add(nums[j]); + } + + // Update answer by adding current cycle. + if (cycle_size > 0) { + ans += (cycle_size - 1); + if (cycle_size > 1) { + results.add(temp); + } + } + } + for (List t : results) { + System.out.println(Arrays.toString(t.toArray())); + } + return ans; + } + + public static void swap(int[] arr, int i, int j) { + int temp = arr[i]; + arr[i] = arr[j]; + arr[j] = temp; + } + + public static void swap(ArrayList list, int i, int j) { + Pair temp = list.get(i); + list.set(i, list.get(j)); + list.set(j, temp); + } + + /** + * tricky sort + */ + public static int minimumSwaps(int[] nums) { + + ArrayList pairs = new ArrayList<>(); + + for (int i = 0; i < nums.length; i++) { + pairs.add(new Pair(i, nums[i])); + } + + pairs.sort(Comparator.comparingInt(a -> a.val)); + int result = 0; + for (int i = 0; i < nums.length; i++) { + if (i == pairs.get(i).idx) continue; + + result++; + swap(pairs, pairs.get(i).idx, i); + i -= 1; + } + return result; + } + + public static void main(String[] args) { + System.out.println(minSwaps(new int[]{10, 19, 6, 3, 5}) + " - " + minimumSwaps(new int[]{10, 19, 6, 3, 5})); + } + + static class Pair { + int idx; + int val; + + public Pair(int idx, int val) { + this.idx = idx; + this.val = val; + } + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/MinimumWindowSubsequence.java b/src/main/java/practiceproblems/MinimumWindowSubsequence.java new file mode 100644 index 0000000..cf40447 --- /dev/null +++ b/src/main/java/practiceproblems/MinimumWindowSubsequence.java @@ -0,0 +1,80 @@ +package practiceproblems; + +/** + * Input: + * S = "abcdebdde", T = "bde" + * Output: "bcde" + * Explanation: + * "bcde" is the answer because it occurs before "bdde" which has the same length. + * "deb" is not a smaller window because the elements of T in the window must occur in order. + */ +// unresolved +public class MinimumWindowSubsequence { + public String minWindow(String S, String T) { + if (S.length() == 0 || T.length() == 0) { + return ""; + } + + /** + * we can conduct two steps by using two pointers for this probelm: + * 1. check feasibility from left to right + * 2. check optimization from right to left + * we can traverse from left to right, find a possible candidate until reach the first ending character of T + * eg: for the string s = abcdebdde and t = bde, we should traverse s string until we find first e, + * i.e. abcde, then traverse back from current "e" to find if we have other combination of bde with smaller + * length. + * @param right: fast pointer that always points the last character of T in S + * @param left: slow pointer that used to traverse back when right pointer find the last character of T in S + * @param tIndex: third pointer used to scan string T + * @param minLen: current minimum length of subsequence + * */ + int right = 0; + int minLen = Integer.MAX_VALUE; + String result = ""; + + while (right < S.length()) { + int tIndex = 0; + // use fast pointer to find the last character of T in S + while (right < S.length()) { + if (S.charAt(right) == T.charAt(tIndex)) { + tIndex++; + } + if (tIndex == T.length()) { + break; + } + right++; + } + + // if right pointer is over than boundary + if (right == S.length()) { + break; + } + + // use another slow pointer to traverse from right to left until find first character of T in S + int left = right; + tIndex = T.length() - 1; + while (left >= 0) { + if (S.charAt(left) == T.charAt(tIndex)) { + tIndex--; + } + if (tIndex < 0) { + break; + } + left--; + } + // if we found another subsequence with smaller length, update result + if (right - left + 1 < minLen) { + minLen = right - left + 1; + result = S.substring(left, right + 1); + } + // WARNING: we have to move right pointer to the next position of left pointer, NOT the next position + // of right pointer + right = left + 1; + } + return result; + } + + public static void main(String[] args) { + System.out.printf(new MinimumWindowSubsequence().minWindow("abcdebdde","bcde")); + } +} diff --git a/src/main/java/practiceproblems/MinimumWindowSubstring.java b/src/main/java/practiceproblems/MinimumWindowSubstring.java new file mode 100644 index 0000000..67a0ae5 --- /dev/null +++ b/src/main/java/practiceproblems/MinimumWindowSubstring.java @@ -0,0 +1,62 @@ +package practiceproblems; + +import java.util.HashMap; +import java.util.Map; + +/** + * https://leetcode.com/problems/minimum-window-substring/ + */ +class MinimumWindowSubstring { + + public static void main(String[] args) { + System.out.println(minWindow("AADOBECODEBANC", "ABC")); + System.out.println(minWindow("abcdebdde", "bde")); + } + + public static String minWindow(String s, String t) { + if (t.length() > s.length()) { + return ""; + } + Map map = new HashMap<>(); + for (char c : t.toCharArray()) { + map.put(c, map.getOrDefault(c, 0) + 1); + } + + int counter = map.size(); + + int begin = 0, end = 0; + int head = 0; + int len = Integer.MAX_VALUE; + + while (end < s.length()) { + char c = s.charAt(end); + if (map.containsKey(c)) { + map.put(c, map.get(c) - 1); + if (map.get(c) == 0) { + counter--; + } + } + end++; + + while (counter == 0) { + char tempc = s.charAt(begin); + if (map.containsKey(tempc)) { + map.put(tempc, map.get(tempc) + 1); + if (map.get(tempc) > 0) { + counter++; + } + } + if (end - begin < len) { + len = end - begin; + head = begin; + } + begin++; + } + + } + if (len == Integer.MAX_VALUE) { + return ""; + } + return s.substring(head, head + len); + } + } diff --git a/src/geeksforgeeks/MirrorBinaryTree.java b/src/main/java/practiceproblems/MirrorBinaryTree.java similarity index 63% rename from src/geeksforgeeks/MirrorBinaryTree.java rename to src/main/java/practiceproblems/MirrorBinaryTree.java index 4b166ed..2c3c6ae 100644 --- a/src/geeksforgeeks/MirrorBinaryTree.java +++ b/src/main/java/practiceproblems/MirrorBinaryTree.java @@ -1,8 +1,12 @@ -package geeksforgeeks;// Iterative Java program to convert a Binary -// Tree to its mirror +package practiceproblems; import java.util.*; +/** + * https://www.geeksforgeeks.org/write-an-efficient-c-function-to-convert-a-tree-into-its-mirror-tree/ + *

+ * Iterative Java program to convert a Binary Tree to its mirror + */ class MirrorBinaryTree { static class Node { @@ -11,7 +15,6 @@ static class Node { Node right; } - static Node newNode(int data) { Node node = new Node(); node.data = data; @@ -20,37 +23,37 @@ static Node newNode(int data) { } static void mirror(Node root) { - if (root == null) + if (root == null) { return; + } Queue q = new LinkedList<>(); q.add(root); - while (q.size() > 0) { - Node curr = q.peek(); - q.remove(); - + while (!q.isEmpty()) { + Node curr = q.poll(); Node temp = curr.left; curr.left = curr.right; curr.right = temp; - if (curr.left != null) + if (curr.left != null) { q.add(curr.left); - if (curr.right != null) + } + if (curr.right != null) { q.add(curr.right); + } } } - static void inOrder(Node node) { - if (node == null) + if (node == null) { return; + } inOrder(node.left); System.out.print(node.data + " "); inOrder(node.right); } - public static void main(String args[]) { Node root = newNode(1); root.left = newNode(2); @@ -58,14 +61,12 @@ public static void main(String args[]) { root.left.left = newNode(4); root.left.right = newNode(5); - System.out.print("\n Inorder traversal of the" - + " coned tree is \n"); + System.out.print("\n Inorder traversal of the" + " coned tree is \n"); inOrder(root); mirror(root); - System.out.print("\n Inorder traversal of the " + - "mirror tree is \n"); + System.out.print("\n Inorder traversal of the " + "mirror tree is \n"); inOrder(root); } } diff --git a/src/main/java/practiceproblems/MobileKeyPadCombinations.java b/src/main/java/practiceproblems/MobileKeyPadCombinations.java new file mode 100644 index 0000000..12488af --- /dev/null +++ b/src/main/java/practiceproblems/MobileKeyPadCombinations.java @@ -0,0 +1,59 @@ +package practiceproblems; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; + +/** + * https://www.geeksforgeeks.org/find-possible-words-phone-digits/ + */ +public class MobileKeyPadCombinations { + + public List letterCombinations(String digits) { + List result = new ArrayList<>(); + if (digits == null || digits.length() == 0) { + return result; + } + + String[] keypad = { "--", "00", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz" }; + + generateCombinations(digits, keypad, digits.length(), 0, new StringBuilder(), result); + return result; + } + + public void generateCombinations(String digits, String[] keypad, int len, int startIndex, StringBuilder sb, + List result) { + if (startIndex == len) { + result.add(sb.toString()); + return; + } + + for (char ch : keypad[digits.charAt(startIndex) - '0'].toCharArray()) { + sb.append(ch); + generateCombinations(digits, keypad, len, startIndex + 1, sb, result); + sb.deleteCharAt(sb.length() - 1); + } + } + + public static void main(String[] args) { + new MobileKeyPadCombinations().letterCombinations("23").forEach(System.out::println); + } + + public List letterCombinationsIterative(String digits) { + LinkedList ans = new LinkedList<>(); + if (digits.isEmpty()) { + return ans; + } + String[] mapping = { "0", "1", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz" }; + ans.add(""); + for (int i = 0; i < digits.length(); i++) { + int x = Character.getNumericValue(digits.charAt(i)); + while (ans.peek().length() == i) { + String t = ans.remove(); + for (char s : mapping[x].toCharArray()) + ans.add(t + s); + } + } + return ans; + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/MoveZeroes.java b/src/main/java/practiceproblems/MoveZeroes.java new file mode 100644 index 0000000..b347165 --- /dev/null +++ b/src/main/java/practiceproblems/MoveZeroes.java @@ -0,0 +1,34 @@ +package practiceproblems; + +import java.util.Arrays; + +/** + * https://leetcode.com/problems/move-zeroes/ + * [[4,2,4,0,0,3,0,5,1,0]] + */ +class MoveZeroes { + + public static void main(String[] args) { + MoveZeroes mz = new MoveZeroes(); + int[] arr = { 0, 1, 0, 3, 12 }; + mz.moveZeroes(arr); + System.out.println(Arrays.toString(arr)); + } + + public void moveZeroes(int[] nums) { + if (nums == null || nums.length == 0) { + return; + } + + int insertPos = 0; + for (int num : nums) { + if (num != 0) { + nums[insertPos++] = num; + } + } + + while (insertPos < nums.length) { + nums[insertPos++] = 0; + } + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/MultiplyTwoNumbers.java b/src/main/java/practiceproblems/MultiplyTwoNumbers.java new file mode 100644 index 0000000..0cd14fc --- /dev/null +++ b/src/main/java/practiceproblems/MultiplyTwoNumbers.java @@ -0,0 +1,59 @@ +package practiceproblems; + +import java.util.Arrays; + +/** + * https://leetcode.com/problems/multiply-strings/ + * + * `num1[i] * num2[j]` will be placed at indices `[i + j`, `i + j + 1]` + */ +public class MultiplyTwoNumbers { + + public static String multiply(String num1, String num2) { + int m = num1.length(), n = num2.length(); + int[] pos = new int[m + n]; + + /** + + 121 X + 23 + ------------ + 363 + 242 + ------ + + */ + for (int i = m - 1; i >= 0; i--) { + for (int j = n - 1; j >= 0; j--) { + int a = (num1.charAt(i) - '0'); + int b = (num2.charAt(j) - '0'); + int multipliedVal = a * b; + int valuePlace = i + j; + int carryOverPlace = i + j + 1; + + // add carryover to the multiplied value + int sum = multipliedVal + pos[carryOverPlace]; // pos[carryOverPlace] will act as reminder also + + pos[valuePlace] += sum / 10; + pos[carryOverPlace] = (sum) % 10; + System.out.println("a : " + a + " b: " + b + " valuePlace :" + valuePlace + " carryOverPlace " + carryOverPlace); + System.out.println("At end of j " + j + " arr vals are " + Arrays.toString(pos)); + System.out.println(); + } + System.out.println(); + System.out.println("At end of i " + i + " arr vals are " + Arrays.toString(pos)); + System.out.println(); + } + + StringBuilder sb = new StringBuilder(); + for (int p : pos) sb.append(p); + + while (sb.length() != 0 && sb.charAt(0) == '0') sb.deleteCharAt(0); + return sb.length() == 0 ? "0" : sb.toString(); + } + + public static void main(String[] args) { + System.out.println(multiply("123", "62")); + } + +} diff --git a/src/main/java/practiceproblems/NextPermutation.java b/src/main/java/practiceproblems/NextPermutation.java new file mode 100644 index 0000000..7349376 --- /dev/null +++ b/src/main/java/practiceproblems/NextPermutation.java @@ -0,0 +1,64 @@ +package practiceproblems; + +/** + * https://leetcode.com/problems/next-permutation + * https://www.youtube.com/watch?v=LuLCLgMElus + */ +public class NextPermutation { + public void nextPermutation(int[] nums) { + if (nums.length == 0) + return; + + //Find the break-point, i:first index i from the back of the given array arr[i] becomes smaller than arr[i+1]. + //For example, if the given array is {2,1,5,4,3,0,0}, the break-point will be index 1(0-based indexing). + // Here from the back of the array, index 1 is the first index where arr[1] i.e. 1 is smaller than arr[i+1] i.e. 5. + int idx = -1; + int n = nums.length; + // the reason for starting backwards is we need to find a sub-array in descending order + for (int i = n - 2; i >= 0; i--) { + if (nums[i] < nums[i + 1]) { + idx = i; + break; + } + } + + if (idx == -1) { + //If such a break-point does not exist i.e. if the array is sorted in decreasing order, + // the given permutation is the last one in the sorted order of all possible permutations. + // So, the next permutation must be the first i.e. the permutation in increasing order. + //So, in this case, we will reverse the whole array and will return it as our answer. + for (int i = 0; i < n / 2; i++) { + swap(nums, i, n - i - 1); + } + return; + } + + //Find the smallest number i.e. > arr[i] and in the right half of index i + // (i.e. from index i+1 to n-1) and swap it with arr[i]. + for (int i = n - 1; i >= idx; i--) { + if (nums[i] > nums[idx]) { + swap(nums, i, idx); + break; + } + } + + //Reverse the entire right half(i.e. from index i+1 to n-1) of index i. And finally, return the array. + int start = idx + 1; + int end = n - 1; + while (start < end) { + swap(nums, start, end); + start++; + end--; + } + return; + + } + + void swap(int[] nums, int i, int j) { + int temp = nums[i]; + nums[i] = nums[j]; + nums[j] = temp; + } + + +} diff --git a/src/main/java/practiceproblems/NonDecreasingArray.java b/src/main/java/practiceproblems/NonDecreasingArray.java new file mode 100644 index 0000000..cbec61d --- /dev/null +++ b/src/main/java/practiceproblems/NonDecreasingArray.java @@ -0,0 +1,47 @@ +package practiceproblems; + +/** + * https://leetcode.com/problems/non-decreasing-array/description/ + * + * tricky + */ +class NonDecreasingArray { + /** + * Whenever we encounter a violation at a particular index i, we need to check what modification we can make to make the array sorted + * [3,4,5,3,6,8] + * In the example above, we consider the numbers 4, 5, 3 for deciding on how to fix the violation or. + * In this case, the correct modification is to change the number 3 to 5. + * If we change 5 to 3, then we won't be fixing the violation because the resulting array would be 3, 4, 3, 3, 6, 8. + * The basic decision-making process for fixing a violation is listed below. + * Without considering the number at the index i - 2, we won't be able to choose between updating nums[i] or nums[i - 1] + * + * in the example above nums[i-2] <= nums[i] so there's no point in changing nums[i-1] to nums[i] (i.e 5 to 3) + * so we change the nums[i] to nums[i-1] (i.e 3 to 5) + * + * @param nums + * @return + */ + public boolean checkPossibility(int[] nums) { + + int count = 0; + for (int i = 1; i < nums.length && count <= 1; i++) { + if (nums[i - 1] > nums[i]) { + count++; + if ((i - 2 < 0) || nums[i - 2] <= nums[i]) { + nums[i - 1] = nums[i]; + } else { + nums[i] = nums[i - 1]; + } + } + } + return count <= 1; + } + + public static void main(String[] args) { + // 1,4,2,3 + // //3,4,2,3 + int[] nums = {7, 8, 2, 3}; + NonDecreasingArray nda = new NonDecreasingArray(); + System.out.println(nda.checkPossibility(nums)); + } +} diff --git a/src/main/java/practiceproblems/NumberOfBallons.java b/src/main/java/practiceproblems/NumberOfBallons.java new file mode 100644 index 0000000..1546b82 --- /dev/null +++ b/src/main/java/practiceproblems/NumberOfBallons.java @@ -0,0 +1,56 @@ +package practiceproblems; + +/** + * https://leetcode.com/problems/maximum-number-of-balloons/discuss/382401/WithComments-StraightForward-Java-Simple-count-of-chars + */ +public class NumberOfBallons { + + public static int maxNumberOfBalloons(String text) { + int[] chars = new int[26]; //count all letters + for (char c : text.toCharArray()) { + chars[c - 'a']++; + } + int min = chars[1];//for b + min = Math.min(min, chars[0]);//for a + min = Math.min(min, chars[11] / 2);// for l /2 + min = Math.min(min, chars[14] / 2);//similarly for o/2 + min = Math.min(min, chars[13]);//for n + return min; + } + + private int findMaxNumberofPattern(String text, String pattern) { + int n = text.length(), m = pattern.length(), answer = Integer.MAX_VALUE; + int freqInText[] = new int[26]; + int freqInPattern[] = new int[26]; + + // Frequency of characters in text. + for (int i = 0; i < n; i++) { + freqInText[text.charAt(i) - 'a']++; + } + // Frequency of characters in pattern. + for (int i = 0; i < m; i++) { + freqInPattern[pattern.charAt(i) - 'a']++; + } + + // Compare the maximum string that can be produced + // considering one character at a time. + for (int i = 0; i < 26; i++) { + // Do not divide by zero. + if (freqInPattern[i] > 0) { + answer = Math.min(answer, freqInText[i] / freqInPattern[i]); + } + } + + return answer; + } + + public int maxNumberOfBalloonsAnother(String text) { + // Any string made up of lowercase English letters. + String pattern = "balloon"; + return findMaxNumberofPattern(text, pattern); + } + + public static void main(String[] args) { + maxNumberOfBalloons("llonbioan"); + } +} diff --git a/src/main/java/practiceproblems/NumberOfSubMatrices.java b/src/main/java/practiceproblems/NumberOfSubMatrices.java new file mode 100644 index 0000000..6cc1129 --- /dev/null +++ b/src/main/java/practiceproblems/NumberOfSubMatrices.java @@ -0,0 +1,53 @@ +package practiceproblems; + +import java.util.Arrays; + +/** + * TODO + * https://leetcode.com/problems/count-submatrices-with-all-ones/ + */ +public class NumberOfSubMatrices { + public static int numSubmat(int[][] arr) { + int n = arr.length; + int m = arr[0].length; + int brr[][] = new int[n][m]; + + // these are the loops for making a matrix brr[][] + // where index brr[i][j] will contain continuous number of 1 + //starting from index j to n-1 + for (int i = 0; i < n; ++i) { + brr[i][0] = arr[i][0]; + for (int j = 1; j < m; ++j) { + if (arr[i][j] == 1) { + brr[i][j] = 1 + brr[i][j - 1]; + } + } + } + // these are the loops for finding number of submatrices of all 1 + // starting from a particular fixed index (i,j) and adding it to answer + // we do this step for all i,j + + for (int i = 0; i < n; ++i) { + System.out.println(Arrays.toString(brr[i])); + } + int ans = 0; + for (int i = 0; i < n; ++i) { + for (int j = 0; j < m; ++j) { + ans += brr[i][j]; + int min = brr[i][j]; + for (int ii = i + 1; ii < n; ++ii) { + min = Math.min(min, brr[ii][j]); + ans += min; + } + } + } + return ans; + + } + + public static void main(String[] args) { + numSubmat(new int[][]{{1, 0, 1}, + {1, 1, 0}, + {1, 1, 0}}); + } +} diff --git a/src/main/java/practiceproblems/NutsAndBoltsMatch.java b/src/main/java/practiceproblems/NutsAndBoltsMatch.java new file mode 100644 index 0000000..fb7035c --- /dev/null +++ b/src/main/java/practiceproblems/NutsAndBoltsMatch.java @@ -0,0 +1,78 @@ +package practiceproblems; + +import java.util.Arrays; + +/** + * TODO + * http://www.lintcode.com/en/problem/nuts-bolts-problem/ + */ +public class NutsAndBoltsMatch { + /** + * @param nuts: an array of integers + * @param bolts: an array of integers + * @param compare: a instance of Comparator + */ + public void sortNutsAndBolts(String[] nuts, String[] bolts, NBComparator compare) { + if (nuts == null || bolts == null) return; + if (nuts.length != bolts.length) return; + + qsort(nuts, bolts, compare, 0, nuts.length - 1); + + System.out.println(Arrays.toString(nuts)); + System.out.println(Arrays.toString(bolts)); + } + + private void qsort(String[] nuts, String[] bolts, NBComparator compare, + int l, int u) { + if (l >= u) return; + // find the partition index for nuts with bolts[l] + int partitionIndex = partition(nuts, bolts[l], compare, l, u); + // partition bolts with nuts[part_inx] + partition(bolts, nuts[partitionIndex], compare, l, u); + // qsort recursively + qsort(nuts, bolts, compare, l, partitionIndex - 1); + qsort(nuts, bolts, compare, partitionIndex + 1, u); + } + + private int partition(String[] str, String pivot, NBComparator compare, + int lowerIndex, int upperIndex) { + // + int m = lowerIndex; + for (int i = lowerIndex + 1; i <= upperIndex; i++) { + if (compare.cmp(str[i], pivot) == -1 || + compare.cmp(pivot, str[i]) == 1) { + // + m++; + swap(str, i, m); + } else if (compare.cmp(str[i], pivot) == 0 || + compare.cmp(pivot, str[i]) == 0) { + // swap nuts[l]/bolts[l] with pivot + swap(str, i, lowerIndex); + i--; + } + } + // move pivot to proper index + swap(str, m, lowerIndex); + + return m; + } + + private void swap(String[] str, int l, int r) { + String temp = str[l]; + str[l] = str[r]; + str[r] = temp; + } + + public static void main(String[] args) { + new NutsAndBoltsMatch().sortNutsAndBolts(new String[]{"ab", "bc", "dd", "gg"}, new String[]{"AB", "GG", "DD", "BC"}, new NBComparator()); + } + + static class NBComparator { + public int cmp(String a, String b) { + return a.compareTo(b); + } + } +} + + + diff --git a/src/main/java/practiceproblems/PairDivisibleBy60.java b/src/main/java/practiceproblems/PairDivisibleBy60.java new file mode 100644 index 0000000..4af0264 --- /dev/null +++ b/src/main/java/practiceproblems/PairDivisibleBy60.java @@ -0,0 +1,42 @@ +package practiceproblems; + +/** + * https://leetcode.com/problems/pairs-of-songs-with-total-durations-divisible-by-60 + */ +public class PairDivisibleBy60 { + public int numPairsDivisibleBy60(int[] time) { + //brute force - O(n ^ 2) time - TLE + // int n = time.length; + // int result = 0; + // for(int i = 0; i < n; i++){ + // for(int j = i + 1; j < n; j++){ + // if((time[i] + time[j]) % 60 == 0) + // result++; + // } + // } + // return result; + + //2 SUM PROBLEM WITH K = 60 + //O(n) time + /** + * the solution is (time[i]+time[j]%60 == 0) + * if we replace time[j] = 20 + * then we can have time[i] as either 40 , 100 or 160 + * the 40, 100 and 160 if we mod with 60 the results would be 40 + * + * let's take this arr = [60, 30, 20, 150, 120, 100, 30] + * if we mod with 60 then the arr would look like [0, 30, 20, 30, 0, 40, 30] + */ + int[] c = new int[60]; //because we know its going upto 0 to 59 + int result = 0; + for (int t : time) { + // System.out.println(t % 60) + if (t % 60 == 0) //completely divisible on its own + result += c[0]; + else + result += c[60 - t % 60]; //to keep it in range + c[t % 60] += 1; + } + return result; + } +} diff --git a/src/main/java/practiceproblems/PalindromePartition.java b/src/main/java/practiceproblems/PalindromePartition.java new file mode 100644 index 0000000..eebe0af --- /dev/null +++ b/src/main/java/practiceproblems/PalindromePartition.java @@ -0,0 +1,58 @@ +package practiceproblems; + +import java.util.ArrayList; +import java.util.List; + + +/** + * tricky permutation + * + * https://leetcode.com/problems/palindrome-partitioning/ + * Given a string s, partition s such that every substring of the partition is a palindrome. + *

+ * Return all possible palindrome partitioning of s. + *

+ * Input: "aab" + * Output: + * [ + * ["aa","b"], + * ["a","a","b"] + * ] + */ +public class PalindromePartition { + public List> partition(String s) { + List> res = new ArrayList<>(); + List list = new ArrayList<>(); + dfs(s, 0, list, res); + return res; + } + + public void dfs(String s, int pos, List list, List> res) { + if (pos == s.length()) { + res.add(new ArrayList<>(list)); + return; + } + + for (int i = pos; i < s.length(); i++) { + if (isPal(s, pos, i)) { // what we are checking over here is, + // if we partition the string from index to i Example-(0, 0) is palindrome or not + + list.add(s.substring(pos, i + 1)); // take the substring and store it in our list & call the next substring from index + 1 + dfs(s, i + 1, list, res); + list.remove(list.size() - 1); + } + } + + } + + public boolean isPal(String s, int low, int high) { + while (low < high) if (s.charAt(low++) != s.charAt(high--)) return false; + return true; + } + + public static void main(String[] args) { + new PalindromePartition().partition("aab"); + } + + +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/PartitionArrayDisjoint.java b/src/main/java/practiceproblems/PartitionArrayDisjoint.java new file mode 100644 index 0000000..1ba1cec --- /dev/null +++ b/src/main/java/practiceproblems/PartitionArrayDisjoint.java @@ -0,0 +1,63 @@ +package practiceproblems; + +/** + * https://leetcode.com/problems/partition-array-into-disjoint-intervals + */ +public class PartitionArrayDisjoint { + + /** + * We need to divide the array into 2 parts such that max element on the left side is smaller or equal to every element on the right + * We can maintain a maxLeft to keep track of this. + * Now if we come across any number num[i] which is smaller than the maxLeft, this number cannot be part of the right side. + * Thus we need to update the maxLeft and the index of our partition p + * Regarding updating our index, its straight forward to update to i since the ith element has to be part of left side. + * Now what would be the updated maxLeft. + * This has to simply be the value of the maximum element encountered so far + * since this element was present before ith index so has to be the maxLeft now. + * Thus we will keep track of a max and update maxLeft to this value whenever we encounter a smaller number than maxLeft + * + * @param nums + * @return + */ + public int partitionDisjointEff(int[] nums) { + int maxLeft = nums[0]; + int max = nums[0]; + int p = 0; + for (int i = 1; i < nums.length; i++) { + if (nums[i] < maxLeft) { + maxLeft = max; + p = i; + } else if (max < nums[i]) { + max = nums[i]; + } + } + return p + 1; + } + + public int partitionDisjoint(int[] nums) { + int[] left = new int[nums.length]; + int[] right = new int[nums.length]; + left[0] = nums[0]; + + //If the maximum from left array is less than minimum from right array then we can make a split at that idx. + for (int i = 1; i < nums.length; i++) { + left[i] = Math.max(left[i - 1], nums[i]); + } + + right[nums.length - 1] = nums[nums.length - 1]; + + for (int i = nums.length - 2; i >= 0; i--) { + right[i] = Math.min(nums[i], right[i + 1]); + } + + for (int i = 0; i < nums.length - 1; i++) { + if (left[i] <= right[i + 1]) return i + 1; + } + + return -1; + } + + public static void main(String[] args) { + System.out.println(new PartitionArrayDisjoint().partitionDisjointEff(new int[]{5, 0, 3, 8, 6, 1,9})); + } +} diff --git a/src/main/java/practiceproblems/PartitionLabel.java b/src/main/java/practiceproblems/PartitionLabel.java new file mode 100644 index 0000000..7975ab3 --- /dev/null +++ b/src/main/java/practiceproblems/PartitionLabel.java @@ -0,0 +1,50 @@ +package practiceproblems; + +import java.util.ArrayList; +import java.util.List; + +/** + * https://leetcode.com/problems/partition-labels/ + * A string S of lowercase English letters is given. + * We want to partition this string into as many parts as possible + * so that each letter appears in at most one part, and return a list of integers representing the size of these parts. + * + * Input: S = "ababcbacadefegdehijhklij" + * Output: [9,7,8] + * Explanation: + * The partition is "ababcbaca", "defegde", "hijhklij". + * This is a partition so that each letter appears in at most one part. + * A partition like "ababcbacadefegde", "hijhklij" is incorrect, because it splits S into less parts. + */ +class PartitionLabel { + + public List partitionLabels(String str) { + + int[] last = new int[26]; + for (int i = 0; i < str.length(); ++i) + // find last appearance of the char + last[str.charAt(i) - 'a'] = i; + + int j = 0; + int anchor = 0; + List ans = new ArrayList<>(); + for (int i = 0; i < str.length(); ++i) { + // find the max of j and last appearance of str[i] + j = Math.max(j, last[str.charAt(i) - 'a']); + if (i == j) { + // it is (1+i-anchor) + int length = i - anchor + 1; + ans.add(length); + anchor = i + 1; + } + } + return ans; + } + + public static void main(String[] args) { + String S = "ababcbacadefegdehijhklij"; + PartitionLabel partition = new PartitionLabel(); + System.out.println(partition.partitionLabels(S)); + + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/PascalsTriangle.java b/src/main/java/practiceproblems/PascalsTriangle.java new file mode 100644 index 0000000..36b7ef1 --- /dev/null +++ b/src/main/java/practiceproblems/PascalsTriangle.java @@ -0,0 +1,67 @@ +package practiceproblems; + +import java.util.ArrayList; +import java.util.List; + +/** + * https://leetcode.com/problems/pascals-triangle/ + *

+ * Input: 5 + * Output: + * [ + * [1], + * [1,1], + * [1,2,1], + * [1,3,3,1], + * [1,4,6,4,1] + * ] + */ +public class PascalsTriangle { + + public List> generate1(int numRows) { + List> allrows = new ArrayList<>(); + ArrayList row = new ArrayList<>(); + for (int i = 0; i < numRows; i++) { + // suppose if the row is [1,3,3,1] + // add 1 at start [1,1,3,3,1] + // then add together j and j+1 elements like below + // [1,4,3,3,1] => [1,4,6,3,1]=>[1,4,6,4,1] + row.add(0, 1); + for (int j = 1; j < row.size() - 1; j++) + row.set(j, row.get(j) + row.get(j + 1)); + allrows.add(new ArrayList<>(row));// every time the copy is only appended + } + return allrows; + + } + + public List> generate(int numRows) { + List> triangle = new ArrayList<>(); + + // Base case; first row is always [1]. + triangle.add(new ArrayList<>()); + triangle.get(0).add(1); + + for (int rowNum = 1; rowNum < numRows; rowNum++) { + List row = new ArrayList<>(); + List prevRow = triangle.get(rowNum - 1); + + // The first row element is always 1. + row.add(1); + + // Each triangle element (other than the first and last of each row) + // is equal to the sum of the elements above-and-to-the-left and + // above-and-to-the-right. + for (int j = 1; j < rowNum; j++) { + row.add(prevRow.get(j - 1) + prevRow.get(j)); + } + + // The last row element is always 1. + row.add(1); + + triangle.add(row); + } + + return triangle; + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/PermutationInString.java b/src/main/java/practiceproblems/PermutationInString.java new file mode 100644 index 0000000..e277156 --- /dev/null +++ b/src/main/java/practiceproblems/PermutationInString.java @@ -0,0 +1,57 @@ +package practiceproblems; + +import java.util.Arrays; + +/** + * tricky sliding window + * + * Given two strings s1 and s2, write a function to return true if s2 contains the permutation of s1. + * In other words, one of the first string's permutations is the substring of the second string. + * Input:s1= "ab" s2 = "eidboaoo" + * Output: False + *

+ * https://leetcode.com/problems/permutation-in-string/ + */ +public class PermutationInString { + + // How do we know string p is a permutation of string s? Easy, each character in p is in s too. + // So we can abstract all permutation strings of s to a map (Character -> Count). i.e. abba -> {a:2, b:2}. + // Since there are only 26 lower case letters in this problem, + // we can just use an array to represent the map. + // How do we know string s2 contains a permutation of s1? + // We just need to create a sliding window with length of s1, + // move from beginning to the end of s2. + // When a character moves in from right of the window, + // we subtract 1 to that character count from the map. + // When a character moves out from left of the window, we add 1 to that character count. + // So once we see all zeros in the map, + // meaning equal numbers of every characters between s1 and + // the substring in the sliding window, we know the answer is true. + public static boolean checkInclusion(String s1, String s2) { + if (s1.length() == 0 || s2.length() == 0) { + return false; + } + int[] cache = new int[26]; + for (char s : s1.toCharArray()) { + cache[s - 'a']++; + } + + int right = 0; + + while (right < s2.length()) { + cache[s2.charAt(right) - 'a']--; + if (right >= s1.length()) { + cache[s2.charAt(right - s1.length()) - 'a']++; // sliding the window + } + if (Arrays.stream(cache).sum()==0) { + return true; + } + right++; + } + return false; + } + + public static void main(String[] args) { + System.out.println(checkInclusion("ab", "eidbaoo")); + } +} \ No newline at end of file diff --git a/src/geeksforgeeks/PetrolGasStation.java b/src/main/java/practiceproblems/PetrolGasStation.java similarity index 90% rename from src/geeksforgeeks/PetrolGasStation.java rename to src/main/java/practiceproblems/PetrolGasStation.java index fea857c..14d5854 100644 --- a/src/geeksforgeeks/PetrolGasStation.java +++ b/src/main/java/practiceproblems/PetrolGasStation.java @@ -1,4 +1,4 @@ -package geeksforgeeks; +package practiceproblems; public class PetrolGasStation { @@ -24,14 +24,13 @@ public static int canCompleteCircuit(int[] gas, int[] cost) { tank = 0; } } - return start; } public static void main(String[] args) { - int[] gas = {4, 6, 7, 4}; - int[] cost = {6, 5, 3, 5}; + int[] gas = { 4, 6, 7, 4 }; + int[] cost = { 6, 5, 3, 5 }; int start = canCompleteCircuit(gas, cost); System.out.println(start == -1 ? "No Solution" : "Start = " + start); } diff --git a/src/main/java/practiceproblems/PlatesBetweenCandles.java b/src/main/java/practiceproblems/PlatesBetweenCandles.java new file mode 100644 index 0000000..971a7ec --- /dev/null +++ b/src/main/java/practiceproblems/PlatesBetweenCandles.java @@ -0,0 +1,82 @@ +package practiceproblems; + +import java.util.TreeSet; + +//https://leetcode.com/problems/plates-between-candles/ +public class PlatesBetweenCandles { + public int[] platesBetweenCandles(String s, int[][] queries) { + int n = s.length(); + int[] nearestRightCandle = new int[n], nearestLeftCandle = new int[n] , starCount = new int[n]; + int candles = -1, stars = 0; + char[] ch = s.toCharArray(); + + for(int i = 0; i< n; i++){ + if(ch[i] == '*'){ + stars++; + } else{ + candles = i; + } + nearestLeftCandle[i] = candles; + starCount[i] = stars; + } + + candles = -1; + for(int i = n-1; i>= 0; i--){ + if(ch[i] == '|'){ + candles = i; + } + nearestRightCandle[i] = candles; + } + int m = queries.length; + int[] res = new int[m]; + + for(int i = 0; i< m; i++){ + int lw = nearestRightCandle[queries[i][0]]; + int rw = nearestLeftCandle[queries[i][1]]; + + if(lw == -1 || rw == -1 || lw >= rw){ + res[i] = 0; + continue; + } + + res[i] = starCount[rw] - starCount[lw]; + } + + return res; + } + public int[] platesBetweenCandlesWithTreeSet(String s, int[][] queries) { + int len = s.length(); + int[] plates = new int[len]; + + TreeSet candles = new TreeSet<>(); + + int plateCount = 0; + + //get the platecount and the indices of the candles + for (int i = 0; i < len; i++) { + if (s.charAt(i) == '|') { + candles.add(i); + plates[i] = plateCount; + } else { + plateCount++; + } + } + + //iterating through the queries + int[] result = new int[queries.length]; + int i = 0; + + for (int[] query : queries) { + Integer leftCandle = candles.ceiling(query[0]); + Integer rightCandle = candles.floor(query[1]); + + if (leftCandle != null && rightCandle != null && leftCandle < rightCandle) { + result[i] = plates[rightCandle] - plates[leftCandle]; + } + i++; + + } + + return result; + } +} diff --git a/src/main/java/practiceproblems/PlusOne.java b/src/main/java/practiceproblems/PlusOne.java new file mode 100644 index 0000000..6096b5f --- /dev/null +++ b/src/main/java/practiceproblems/PlusOne.java @@ -0,0 +1,38 @@ +package practiceproblems; + +import java.util.Arrays; + +/** + * https://leetcode.com/problems/plus-one/ + */ +class PlusOne { + + public static int[] plusOne(int[] digits) { + + int n = digits.length; + // move along the input array starting from the end + for (int idx = n - 1; idx >= 0; --idx) { + // set all the nines at the end of array to zeros + if (digits[idx] == 9) { + digits[idx] = 0; + } + // here we have the rightmost not-nine + else { + // increase this rightmost not-nine by 1 + digits[idx]++; + // and the job is done + return digits; + } + } + // we're here because all the digits are nines + digits = new int[n + 1]; + digits[0] = 1; + return digits; + } + + public static void main(String[] args) { + int[] digits = { 1, 2, 9 }; + int[] endpointUrl = plusOne(digits); + Arrays.stream(endpointUrl).forEach(System.out::println); + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/Pow.java b/src/main/java/practiceproblems/Pow.java new file mode 100644 index 0000000..05c174a --- /dev/null +++ b/src/main/java/practiceproblems/Pow.java @@ -0,0 +1,55 @@ +package practiceproblems; + +/** + * + * tricky + * + * 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 + */ +public class Pow { + + public double myPow(double x, int n) { + + if (n < 0) { + n *= -1; + x = 1 / x; + } + double result = 1; + + result = powerCalcUtil(x, n); + return result; + } + + + public double powerCalcUtil(double x, int n) { + if (n == 0) + return 1; + + double half = powerCalcUtil(x, n / 2); + if (n % 2 == 0) { + return half * half; + } else { + return half * half * x; + } + } + + public double pow1(double x, int n) { + double result = 1.0; + for(int i = n; i != 0; i /= 2, x *= x) { + if( i % 2 != 0 ) { + result *= x; + } + } + return n < 0 ? 1.0 / result : result; + } + +} \ No newline at end of file diff --git a/src/geeksforgeeks/PrisonAfterNDays.java b/src/main/java/practiceproblems/PrisonAfterNDays.java similarity index 80% rename from src/geeksforgeeks/PrisonAfterNDays.java rename to src/main/java/practiceproblems/PrisonAfterNDays.java index db3c747..0528573 100644 --- a/src/geeksforgeeks/PrisonAfterNDays.java +++ b/src/main/java/practiceproblems/PrisonAfterNDays.java @@ -1,4 +1,4 @@ -package geeksforgeeks; +package practiceproblems; import java.util.Arrays; import java.util.HashSet; @@ -7,9 +7,10 @@ */ class PrisonAfterNDays { - public static int[] prisonAfterNDays(int[] cells, int N) { - if (cells == null || cells.length == 0 || N <= 0) return cells; + if (cells == null || cells.length == 0 || N <= 0) { + return cells; + } boolean hasCycle = false; int cycle = 0; HashSet set = new HashSet<>(); @@ -26,7 +27,9 @@ public static int[] prisonAfterNDays(int[] cells, int N) { cells = next; } if (hasCycle) { - N %= cycle; + N %= cycle; // calculating the reminder day after excluding the cycle + // let's say N=10 and we hit cycle at 3, we need 10%3=1 remaining calculation + // to be done for (int i = 0; i < N; i++) { cells = nextDay(cells); } diff --git a/src/main/java/practiceproblems/ProductExceptSelf.java b/src/main/java/practiceproblems/ProductExceptSelf.java new file mode 100644 index 0000000..43b3fee --- /dev/null +++ b/src/main/java/practiceproblems/ProductExceptSelf.java @@ -0,0 +1,55 @@ +package practiceproblems; + +import java.util.Arrays; + +/** + * + * tricky array manipulation + * + * https://leetcode.com/problems/product-of-array-except-self/ + *

+ * Given numbers [2, 3, 4, 5], regarding the third number 4, + * the product of array except 4 is 2*3*5 which consists of two parts: left 2*3 and right 5. + * The product is left*right. We can get lefts and rights + *

+ * Numbers: 2 3 4 5 + * Lefts: 2 2*3 2*3*4 + * Rights: 3*4*5 4*5 5 + *

+ * Let’s fill the empty with 1: + *

+ * Numbers: 2 3 4 5 + * Lefts: 1 2 2*3 2*3*4 + * Rights: 3*4*5 4*5 5 1 + */ +public class ProductExceptSelf { + public static int[] productExceptSelf(int[] nums) { + int n = nums.length; + int[] res = new int[n]; + res[0] = 1; + + for (int i = 1; i < n; i++) { + res[i] = res[i - 1] * nums[i - 1]; + } + // once building the left product instead of having a new array to calculate rightsum + // we use a variable to store last seen value + // for number 2 3 4 5 + // the left product array is 1 2 2*3 2*3*4 + + // for rightProd during step1 2 3 4 5 right is 1 so last product value retained + // 2*3*4 + // during step 2 the right becomes 5(1*nums[i]=>5) + // so for value 4(length-2) the left product is 2*3 multiplying with 5 becomes 2*3*5 + // during step 3 the right is updated to (5*nums[i]=>4) 20 + int right = 1; + for (int i = n - 1; i >= 0; i--) { + res[i] *= right; + right *= nums[i]; + } + return res; + } + + public static void main(String[] args) { + System.out.println(Arrays.toString(productExceptSelf(new int[] { 1, 2, 3, 4, 5 }))); + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/QueensAttackKing.java b/src/main/java/practiceproblems/QueensAttackKing.java new file mode 100644 index 0000000..27d0a7b --- /dev/null +++ b/src/main/java/practiceproblems/QueensAttackKing.java @@ -0,0 +1,56 @@ +package practiceproblems; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +//https://leetcode.com/problems/queens-that-can-attack-the-king/ + +/** + * On an 8x8 chessboard, there can be multiple Black Queens and one White King. + *

+ * Given an array of integer coordinates queens that represents the positions of the Black Queens, + * and a pair of coordinates king that represent the position of the White King, + * return the coordinates of all the queens (in any order) that can attack the King. + */ +class QueensAttackKing { + public List> queensAttacktheKing(int[][] queens, int[] king) { + List> result = new ArrayList<>(); + boolean[][] visited = new boolean[8][8]; + int[][] dirs = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}, {-1, -1}, {1, 1}, {1, -1}, {-1, 1}}; + // first marking all queen positions + for (int[] qu : queens) { + visited[qu[0]][qu[1]] = true; + } + + for (int[] dir : dirs) { + List temp = findQueensPositions(king, dir[0], dir[1], visited); + if (temp != null) result.add(temp); + } + + return result; + + } + + public List findQueensPositions(int[] king, int xDir, int yDir, boolean[][] visited) { + int newX = xDir + king[0]; + int newY = yDir + king[1]; + // going to walk along x,y only not 8 directions at same time + while (newX < 8 && newY < 8 && newX >= 0 && newY >= 0) { + if (visited[newX][newY]) { + return Arrays.asList(newX, newY); // returns when first queen is met in row or column + } + + newX += xDir; + newY += yDir; + + } + + return null; + } + + public static void main(String[] args) { + int[][] queens = {{0, 1}, {1, 0}, {4, 0}, {0, 4}, {3, 3}, {2, 4}}; + int[] king = {0, 0}; + new QueensAttackKing().queensAttacktheKing(queens, king); + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/RaceCarMinSteps.java b/src/main/java/practiceproblems/RaceCarMinSteps.java new file mode 100644 index 0000000..0f9e769 --- /dev/null +++ b/src/main/java/practiceproblems/RaceCarMinSteps.java @@ -0,0 +1,51 @@ +package practiceproblems; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.Queue; +import java.util.Set; + +class RaceCarMinSteps { + public int racecar(int target) { + Set visited = new HashSet<>(); + Queue queue = new LinkedList<>(); + queue.add(new StateNode(1, 0)); + int distance = 0; + while (!queue.isEmpty()) { + int levelSize = queue.size(); + for (int i = 0; i < levelSize; i++) { + StateNode cur = queue.poll(); + if (cur.position == target) { + return distance; + } + // if A + int nextPosition = cur.position + cur.speed; + int nextSpeed = cur.speed * 2; + if (!visited.contains(nextSpeed + "," + nextPosition) && Math.abs(target - nextPosition) < target) { + visited.add(nextSpeed + "," + nextPosition); + queue.offer(new StateNode(nextSpeed, nextPosition)); + } + // if R + nextPosition = cur.position; + nextSpeed = cur.speed > 0 ? -1 : 1; + if (!visited.contains(nextSpeed + "," + nextPosition) && Math.abs(target - nextPosition) < target) { + visited.add(nextSpeed + "," + nextPosition); + queue.offer(new StateNode(nextSpeed, nextPosition)); + } + } + distance++; + } + return -1; + } + + static class StateNode { + int speed; + int position; + + public StateNode(int speed, int position) { + this.speed = speed; + this.position = position; + } + } +} + diff --git a/src/main/java/practiceproblems/RandomLinkedList.java b/src/main/java/practiceproblems/RandomLinkedList.java new file mode 100644 index 0000000..d73db67 --- /dev/null +++ b/src/main/java/practiceproblems/RandomLinkedList.java @@ -0,0 +1,61 @@ +package practiceproblems; + +/** + * https://leetcode.com/problems/copy-list-with-random-pointer/ + */ +class RandomLinkedList { + + public Node copyRandomList(Node head) { + if (head == null) { + return null; + } + Node dummy = new Node(-1); + dummy.next = head; + + while (head != null) { + Node newNode = new Node(head.val); + newNode.next = head.next; + head.next = newNode; + head = newNode.next; + } + + head = dummy.next; + + while (head != null && head.next != null) { + + head.next.random = head.random != null ? head.random.next : null; + head = head.next.next; + } + + Node oldHead = dummy.next; + Node newHead = dummy.next.next; + Node newTemp = newHead; + + while (oldHead != null) { + oldHead.next = oldHead.next.next; + newHead.next = newHead.next == null ? null : newHead.next.next; + + oldHead = oldHead.next; + newHead = newHead.next; + } + + return newTemp; + } + + static class Node { + public int val; + public Node next; + public Node random; + + public Node(int _val) { + val = _val; + next = null; + random = null; + } + + public String toString() { + return "" + this.val; + } + } +} + diff --git a/src/main/java/practiceproblems/RandomPickWithWeight.java b/src/main/java/practiceproblems/RandomPickWithWeight.java new file mode 100644 index 0000000..cf66710 --- /dev/null +++ b/src/main/java/practiceproblems/RandomPickWithWeight.java @@ -0,0 +1,90 @@ +package practiceproblems; + +/** + * https://leetcode.com/problems/random-pick-with-weight/ + * + * tricky + */ +class RandomPickWithWeight { + private int sum; + private int[] sumArr; + + public RandomPickWithWeight(int[] w) { + sum = 0; + sumArr = new int[w.length]; + for (int i = 0; i < w.length; ++i) { + sum += w[i]; + sumArr[i] = sum; + } + } + + /** + * Alternate example to understand the solution: + * + * So lets relate this problem with a problem of dividing a cake at a party + * such that the person with highest weight has better chance to pick a slice.(proportional to his weight) + * + * Suppose we have people with weight 1, 3, 5, 7, 9 pounds, and they went for a party to a bakery and purchased a cake. + * + * They decided that lets add our total weight and buy a cake proportional to it. So their totalWeight came as + * 1+3+5+7+9 = 25 + * They purchased a 25 pound cake :). + * They decided that lets cut cake in 1 pound slices(25 slices total) and whoever wants to eat can take a 1 pound slice at a time. + * The person who will pick a slice will be picked randomly. + * + * To find out how to pick randomly and to figure out who will pick first, they first did a cumulative sum of their weights + * + * 1->1 + * 3-> 1 + 3 = 4 + * 5-> 4 + 5 = 9 + * 7-> 7 + 9 = 16 + * 9-> 9 + 16 = 25 + * + * =1,4,9,16,25 + * + * And then asked the server to pick a random number out of 25 for them. The random number represents a slice. + * So it can be 17 out of 25 or 6 out of 25. + * + * So the slice 1 belongs to 1 pounds person. Which is only 1 slice. + * Slice between 2-4 belong to 3 pounds person. Which are 3 slices. + * . + * . + * Slice between 17- 25 belong to the 9 pounds person. Which are 9 slices. + * + * If we pick a random slice out of 25, + * there is a higher probability that it will belong to a person with 9 slices (the person with 9 pounds) , + * the person who own 7 slices has second highest probability. + * The person whose weight is 1 pound and has only 1 slice has least probability to pick a slice. + * + * And that's what we wanted. + * We want to let the person with highest weight get a greater chance to pick a slice every time even though they are picked at random. + * + * The question can also be asked in reverse + */ + public int pickIndex() { + int idx = (int) (Math.random() * sum); + return binarySearch(idx + 1); // +1 is because the rand will lie between 0-14 + } + + public int binarySearch(int idx) { + int left = 0; + int right = sumArr.length - 1; + + while (left < right) { + int mid = ((right - left) / 2) + left; + if (sumArr[mid] < idx) { + left = mid + 1; + } else { + right = mid; + } + } + return left; + } + + public static void main(String[] args) { + RandomPickWithWeight randomPickWithWeight = new RandomPickWithWeight(new int[]{1, 3, 5, 4, 2}); + randomPickWithWeight.pickIndex(); + + } + +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/RangeSum.java b/src/main/java/practiceproblems/RangeSum.java new file mode 100644 index 0000000..872f765 --- /dev/null +++ b/src/main/java/practiceproblems/RangeSum.java @@ -0,0 +1,37 @@ +package practiceproblems; + +/** + * https://leetcode.com/problems/range-sum-query-immutable/ + * + * First calculate prefix sums array for the given array nums. + * Now, your task is to find sum of elements from left to right. + * If left == 0, means we need to directly return sum at the index pref[right]. + * Otherwise, just return the difference of sums at pref[right] - pref[left - 1]. + * + * Example: + * ar = [-2, 0, 3, -5, 2, -1] + * range = [0, 2], [2, 5] + * + * Prefix Sums Array = [-2,-2,1,-4,-2,-3] + * + * Output: + * For query 1, left == 0, return prefix sum at index right. Print pref[2] = 1 + * For query 2, left != 0, return pref[right] - pref[left - 1]. Print pref[5] - pref[1] = -1 + */ +public class RangeSum { + int[] dp; + + public void NumArray(int[] nums) { + if (nums.length == 0) return; + dp = new int[nums.length]; + dp[0] = nums[0]; + for (int i = 1; i < nums.length; i++) { + dp[i] = dp[i - 1] + nums[i]; + } + } + + public int sumRange(int i, int j) { + if (i == 0) return dp[j]; + return dp[j] - dp[i - 1]; + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/RangeSum2D.java b/src/main/java/practiceproblems/RangeSum2D.java new file mode 100644 index 0000000..8be7967 --- /dev/null +++ b/src/main/java/practiceproblems/RangeSum2D.java @@ -0,0 +1,90 @@ +package practiceproblems; + +/** + * https://leetcode.com/problems/range-sum-query-2d-immutable/ + * https://www.youtube.com/watch?v=PwDqpOMwg6U&ab_channel=TusharRoy-CodingMadeSimple + * +---------------+ +---------+----+ +---+-----------+ +---------+----+ +---+----------+ + * | | | | | | | | | | | | | | + * | (r1,c1) | | | | | | | | | | | | | + * | +------+ | | | | | | | +---------+ | +---+ | + * | | | | = | | | - | | | - | (r1-1,c2) | + | (r1-1,c1-1) | + * | | | | | | | | | | | | | | + * | +------+ | +---------+ | +---+ | | | | | + * | (r2,c2)| | (r2,c2)| | (r2,c1-1) | | | | | + * +---------------+ +--------------+ +---------------+ +--------------+ +--------------+ + */ +public class RangeSum2D { + int[][] dp; + + public RangeSum2D(int[][] matrix) { + int m = matrix.length; + int n = matrix[0].length; + dp = new int[m + 1][n + 1]; + + /** + * +-----+-+-------+ +--------+-----+ +-----+---------+ +-----+--------+ + * | | | | | | | | | | | | | + * | | | | | | | | | | | | | + * +-----+-+ | +--------+ | | | | +-----+ | + * | | | | = | | + | | | - | | + * +-----+-+ | | | +-----+ | | | + * | | | | | | | | + * | | | | | | | | + * +---------------+ +--------------+ +---------------+ +--------------+ + * + * sums[i][j] = sums[i-1][j] + sums[i][j-1] - sums[i-1][j-1] + + * + * matrix[i-1][j-1] + */ + for (int i = 1; i <= m; i++) { + for (int j = 1; j <= n; j++) { + dp[i][j] = dp[i - 1][j] + dp[i][j - 1] - dp[i - 1][j - 1] + matrix[i - 1][j - 1]; + } + } + } + + public int sumRegion(int row1, int col1, int row2, int col2) { + // converting to m+1,n+1 dp[][] index + row1 += 1; + row2 += 1; + col1 += 1; + col2 += 1; + + return dp[row2][col2] - dp[row2][col1 - 1] - dp[row1 - 1][col2] + dp[row1 - 1][col1 - 1]; + } + + /** + * https://leetcode.com/problems/matrix-block-sum + * + * Given a m x n matrix mat and an integer k, return a matrix answer where each answer[i][j] is the sum of all elements mat[r][c] for: + * + * i - k <= r <= i + k, + * j - k <= c <= j + k, and + * (r, c) is a valid position in the matrix. + */ + public int[][] matrixBlockSum(int[][] mat, int K) { + int m = mat.length, n = mat[0].length; + int[][] sum = new int[m + 1][n + 1]; // sum[i][j] is sum of all elements from rectangle (0,0,i,j) as left, top, right, bottom corresponding + for (int r = 1; r <= m; r++) { + for (int c = 1; c <= n; c++) { + sum[r][c] = sum[r - 1][c] + sum[r][c - 1] - sum[r - 1][c - 1] + mat[r - 1][c - 1]; + } + } + int[][] ans = new int[m][n]; + for (int r = 0; r < m; r++) { + for (int c = 0; c < n; c++) { + int r1 = Math.max(0, r - K); + int c1 = Math.max(0, c - K); + int r2 = Math.min(m - 1, r + K); + int c2 = Math.min(n - 1, c + K); + r1++; + c1++; + r2++; + c2++; // Since `sum` start with 1 so we need to increase r1, c1, r2, c2 by 1 + ans[r][c] = sum[r2][c2] - sum[r2][c1 - 1] - sum[r1 - 1][c2] + sum[r1 - 1][c1 - 1]; + } + } + return ans; + } +} + diff --git a/src/main/java/practiceproblems/ReArrangeStringKDistanceApart.java b/src/main/java/practiceproblems/ReArrangeStringKDistanceApart.java new file mode 100644 index 0000000..3f3981f --- /dev/null +++ b/src/main/java/practiceproblems/ReArrangeStringKDistanceApart.java @@ -0,0 +1,47 @@ +package practiceproblems; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.PriorityQueue; + +/** + * https://leetcode.com/problems/rearrange-string-k-distance-apart/ + */ +public class ReArrangeStringKDistanceApart { + + /** + * The greedy algorithm is that in each step, select the char with highest remaining count if possible (if it is not in the waiting queue). + * PQ is used to achieve the greedy. A regular queue waitQueue is used to "freeze" previous appeared char in the period of k. + * + * In each iteration, we need to add current char to the waitQueue and also release the char at front of the queue, put back to maxHeap. + * The "impossible" case happens when the maxHeap is empty but there is still some char in the waitQueue. + * + * The idea of using two Queue is brilliant! By polling entries from the PriorityQueue which are used to construct our result, + * and offering to queue which served as ensuring the distance larger than K, + * we can keep recording all the entries and these entries are "transferred" between two queues. The idea is fantastic. + */ + public String rearrangeString(String s, int k) { + if (k == 0) return s; + int[] freq = new int[26]; + StringBuilder ans = new StringBuilder(); + for (char c : s.toCharArray()) freq[c - 'a']++; + PriorityQueue pq = new PriorityQueue<>((a, b) -> b[1] - a[1]); + for (int i = 0; i < 26; i++) { + if (freq[i] > 0) { + pq.add(new int[]{i, freq[i]}); + } + } + Deque q = new ArrayDeque<>(); + while (!pq.isEmpty()) { + int[] current = pq.poll(); + ans.append((char) (current[0] + 'a')); + current[1]--; + q.offer(current); + if (q.size() == k) { + int[] front = q.poll(); + if (front[1] > 0) pq.offer(front); + } + } + return ans.length() == s.length() ? ans.toString() : ""; + } +} diff --git a/src/main/java/practiceproblems/RemoveAdjacentDuplicates.java b/src/main/java/practiceproblems/RemoveAdjacentDuplicates.java new file mode 100644 index 0000000..64bcf8d --- /dev/null +++ b/src/main/java/practiceproblems/RemoveAdjacentDuplicates.java @@ -0,0 +1,27 @@ +package practiceproblems; + +/** + * https://www.geeksforgeeks.org/recursively-remove-adjacent-duplicates-given-string/ + */ +public class RemoveAdjacentDuplicates { + + + public static void main(String[] args) { + System.out.println(removeDuplicatesLee("azxxzy")); + System.out.println(removeDuplicatesLee("aaaa")); + System.out.println(removeDuplicatesLee("abbaca")); + } + + public static String removeDuplicatesLee(String s) { + int i = 0; + int n = s.length(); + char[] res = s.toCharArray(); + for (int j = 0; j < n; j++, i++) { + res[i] = res[j]; + if (i > 0 && res[i - 1] == res[i]) { + i -= 2; + } + } + return new String(res, 0, i); + } +} \ No newline at end of file diff --git a/src/geeksforgeeks/Node.java b/src/main/java/practiceproblems/RemoveBSTGivenOutsideRange.java similarity index 52% rename from src/geeksforgeeks/Node.java rename to src/main/java/practiceproblems/RemoveBSTGivenOutsideRange.java index 68985c0..b213c83 100644 --- a/src/geeksforgeeks/Node.java +++ b/src/main/java/practiceproblems/RemoveBSTGivenOutsideRange.java @@ -1,10 +1,11 @@ -package geeksforgeeks; +package practiceproblems; +/** + * https://www.geeksforgeeks.org/remove-bst-keys-outside-the-given-range/ + */ +public class RemoveBSTGivenOutsideRange { -class RemoveBSTGivenOutsideRange { - - private static BSTNode removeOutsideRange(BSTNode root, - int min, int max) { + private static BSTNode removeOutsideRange(BSTNode root, int min, int max) { if (root == null) { return null; } @@ -13,42 +14,31 @@ private static BSTNode removeOutsideRange(BSTNode root, root.right = removeOutsideRange(root.right, min, max); if (root.data < min) { - BSTNode rchild = root.right; - root = null; - return rchild; + return root.right; } if (root.data > max) { - BSTNode lchild = root.left; - root = null; - return lchild; + return root.left; } return root; } public static BSTNode newNode(int num) { BSTNode temp = new BSTNode(); - temp.data - = num; + temp.data = num; temp.left = null; temp.right = null; return temp; } - public static BSTNode insert(BSTNode root, - int data) { + public static BSTNode insert(BSTNode root, int data) { if (root == null) { - return newNode(data - ); + return newNode(data); } - if (root.data - > data - ) { - root.left = insert(root.left, data - ); + if (root.data > data) { + root.left = insert(root.left, data); } else { - root.right = insert(root.right, data - ); + root.right = insert(root.right, data); } return root; } @@ -56,8 +46,7 @@ public static BSTNode insert(BSTNode root, private static void inorderTraversal(BSTNode root) { if (root != null) { inorderTraversal(root.left); - System.out.print(root.data - + " "); + System.out.print(root.data + " "); inorderTraversal(root.right); } } @@ -72,20 +61,20 @@ public static void main(String[] args) { root = insert(root, 13); root = insert(root, 7); - System.out.print("Inorder Traversal of " + - "the given tree is: "); + System.out.print("Inorder Traversal of " + "the given tree is: "); inorderTraversal(root); root = removeOutsideRange(root, -10, 13); - System.out.print("\nInorder traversal of " + - "the modified tree: "); + System.out.print("\nInorder traversal of " + "the modified tree: "); inorderTraversal(root); } -} -class BSTNode { - int data; - BSTNode left; - BSTNode right; + private static class BSTNode { + int data; + BSTNode left; + BSTNode right; + } } + + diff --git a/src/main/java/practiceproblems/RemoveDuplicates.java b/src/main/java/practiceproblems/RemoveDuplicates.java new file mode 100644 index 0000000..55bf9f5 --- /dev/null +++ b/src/main/java/practiceproblems/RemoveDuplicates.java @@ -0,0 +1,27 @@ +package practiceproblems; + +/** + * https://leetcode.com/problems/remove-duplicates-from-sorted-array/ + */ +class RemoveDuplicates { + + public int removeDuplicates(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int i = 0; + nums[i++] = nums[0]; + for (int j = 1; j < nums.length; j++) { + if (nums[j] != nums[j - 1]) { + nums[i++] = nums[j]; + } + } + return i; + } + + public static void main(String[] args) { + RemoveDuplicates removeDuplicates = new RemoveDuplicates(); + int[] nums = {1, 1, 2}; + removeDuplicates.removeDuplicates(nums); + } +} \ No newline at end of file diff --git a/src/geeksforgeeks/ReorderLogs.java b/src/main/java/practiceproblems/ReorderLogs.java similarity index 51% rename from src/geeksforgeeks/ReorderLogs.java rename to src/main/java/practiceproblems/ReorderLogs.java index 2140cc1..1772adc 100644 --- a/src/geeksforgeeks/ReorderLogs.java +++ b/src/main/java/practiceproblems/ReorderLogs.java @@ -1,12 +1,30 @@ -package geeksforgeeks; +package practiceproblems; import java.util.Arrays; -/*https://leetcode.com/articles/reorder-log-files/*/ +/** + * https://leetcode.com/problems/reorder-data-in-log-files/ + +You have an array of logs. Each log is a space delimited string of words. + +For each log, the first word in each log is an alphanumeric identifier. Then, either: + +Each word after the identifier will consist only of lowercase letters, or +Each word after the identifier will consist only of digits. +We will call these two varieties of logs letter-logs and digit-logs. It is guaranteed that each log has at least one word after its identifier. + +Reorder the logs so that all of the letter-logs come before any digit-log. +The letter-logs are ordered lexicographically ignoring identifier, with the identifier used in case of ties. +The digit-logs should be put in their original order. + +Input: logs = ["dig1 8 1 5 1","let1 art can","dig2 3 6","let2 own kit dig","let3 art zero"] +Output: ["let1 art can","let3 art zero","let2 own kit dig","dig1 8 1 5 1","dig2 3 6"] +*/ + class ReorderLogs { public static String[] reorderLogFiles(String[] logs) { Arrays.sort(logs, (s1, s2) -> { - String[] split1 = s1.split(" ", 2); + String[] split1 = s1.split(" ", 2); // splits arr in to 2 parts String[] split2 = s2.split(" ", 2); boolean isDigit1 = Character.isDigit(split1[1].charAt(0)); @@ -17,12 +35,12 @@ public static String[] reorderLogFiles(String[] logs) { int comp = split1[1].compareTo(split2[1]); if (comp == 0) return split1[0].compareTo(split2[0]); - else + return comp; } else if (isDigit1 && isDigit2) { // both digit-logs. So keep them in original order return 0; - } else if (isDigit1 && !isDigit2) { + } else if (isDigit1) { // first is digit, second is letter. bring letter to forward. return 1; } else { @@ -34,7 +52,7 @@ public static String[] reorderLogFiles(String[] logs) { } public static void main(String[] args) { - String[] string = {"dig1 8 1 5 1", "let1 art can", "dig2 3 6", "let2 own kit dig", "let3 art zero"}; + String[] string = {"dig1 8 1 5 1", "yet1 art can", "dig2 3 6", "let2 own kit dig", "let3 art zero"}; System.out.println(Arrays.toString(reorderLogFiles(string))); } } \ No newline at end of file diff --git a/src/main/java/practiceproblems/ReorganiseString.java b/src/main/java/practiceproblems/ReorganiseString.java new file mode 100644 index 0000000..a2b77b1 --- /dev/null +++ b/src/main/java/practiceproblems/ReorganiseString.java @@ -0,0 +1,71 @@ +package practiceproblems; + +/** + * Given a string S, check if the letters can be rearranged so that two characters that are adjacent to each other are not the same. + * + * If possible, output any possible result. If not possible, return the empty string. + * + * Input: S = "aab" + * Output: "aba" + * + * Input: S = "aaab" + * Output: "" + */ +class ReorganiseString { + public String reorganizeString(String s) { + if (s == null || s.length() < 1) return ""; + + int[] hash = new int[26]; + for (char c : s.toCharArray()) { + hash[c - 'a']++; + } + + int max = 0; + int letter = 0; + + for (int i = 0; i < hash.length; i++) { + if (hash[i] > max) { + max = hash[i]; + letter = i; + } + } + + if (max > (s.length() + 1) / 2) return ""; + + char[] res = new char[s.length()]; + + int pos = 0; + while (hash[letter] > 0) { + res[pos] = (char) (letter + 'a'); + pos += 2; + hash[letter]--; + } + + // We construct the resulting string in sequence: at position 0, 2, 4, ... and then 1, 3, 5, ... + // In this way, we can make sure there is always a gap between the same characters + + // Consider this example: "aaabbbcdd", we will construct the string in this way: + + // a _ a _ a _ _ _ _ // fill in "a" at position 0, 2, 4 + // a b a _ a _ b _ b // fill in "b" at position 6, 8, 1 + // a b a c a _ b _ b // fill in "c" at position 3 + // a b a c a d b d b // fill in "d" at position 5, 7 + + for (int i = 0; i < hash.length; i++) { + while (hash[i] > 0) { + if (pos >= res.length) pos = 1; + + res[pos] = (char) (i + 'a'); + pos += 2; + hash[i]--; + } + } + + return new String(res); + } + + public static void main(String[] args) { + + new ReorganiseString().reorganizeString("aabccdeeee"); + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/RestoreIpAddresses.java b/src/main/java/practiceproblems/RestoreIpAddresses.java new file mode 100644 index 0000000..bfdeb98 --- /dev/null +++ b/src/main/java/practiceproblems/RestoreIpAddresses.java @@ -0,0 +1,79 @@ +package practiceproblems; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * tricky substring + * + * Given a string s containing only digits, + * return all possible valid IP addresses that can be obtained from s. + * You can return them in any order. + *

+ * A valid IP address consists of exactly four integers, + * each integer is between 0 and 255, separated by single dots and cannot have leading zeros. + * For example, "0.1.2.201" and "192.168.1.1" are valid IP addresses and "0.011.255.245", + * "192.168.1.312" and "192.168@1.1" are invalid IP addresses. + */ + +public class RestoreIpAddresses { + public List restoreIpAddresses(String s) { + if (s == null) + return Collections.emptyList(); + List result = new ArrayList<>(); + + for (int i = 1; i < 4 && i < s.length(); i++) { + String first = s.substring(0, i); + if (isValid(first)) { + for (int j = 1; j < 4 && j + i < s.length(); j++) { + String second = s.substring(i, i + j); + if (isValid(second)) { + for (int k = 1; i + j + k < s.length() && k < 4; k++) { + String third = s.substring(i + j, i + j + k); + String fourth = s.substring(i + j + k); + if (isValid(third) && isValid(fourth)) { + result.add(first + "." + second + "." + third + "." + fourth); + } + } + } + } + } + } + return result; + } + + public boolean isValid(String part) { + if (part == null || part.length() > 3) + return false; + + if (part.charAt(0) == '0' && part.length() > 1) + return false; + + int address = Integer.parseInt(part); + + return address >= 0 && address <= 255; + } + + public List restoreIpAddressesRecur(String s) { + List ans = new ArrayList<>(); + if (s.length() < 4 || s.length() > 12) return ans; + restore(s, ans, "", 0); + return ans; + } + + private void restore(String s, List ans, String restored, int count) { + if (count == 4) { + if (s.isEmpty()) ans.add(restored); + return; + } + + for (int i = 1; i <= Math.min(3, s.length()); i++) { + String sec = s.substring(0, i); + if ((sec.length() > 1 && sec.charAt(0) == '0') || (sec.length() == 3 && Integer.parseInt(sec) > 255)) + continue; + restore(s.substring(i), ans, restored + sec + (count < 3 ? "." : ""), count + 1); + } + + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/ReverseString.java b/src/main/java/practiceproblems/ReverseString.java new file mode 100644 index 0000000..ae1f537 --- /dev/null +++ b/src/main/java/practiceproblems/ReverseString.java @@ -0,0 +1,67 @@ +package practiceproblems; + +/** + * https://leetcode.com/problems/reverse-words-in-a-string/ + * tricky + */ +public class ReverseString { + public StringBuilder trimSpaces(String s) { + int left = 0, right = s.length() - 1; + // remove leading spaces + while (left <= right && s.charAt(left) == ' ') ++left; + + // remove trailing spaces + while (left <= right && s.charAt(right) == ' ') --right; + + // reduce multiple spaces to single one + StringBuilder sb = new StringBuilder(); + while (left <= right) { + char c = s.charAt(left); + + if (c != ' ') + sb.append(c); + else if (sb.charAt(sb.length() - 1) != ' ') + sb.append(c); + + ++left; + } + return sb; + } + + public void reverse(StringBuilder sb, int left, int right) { + while (left < right) { + char tmp = sb.charAt(left); + sb.setCharAt(left++, sb.charAt(right)); + sb.setCharAt(right--, tmp); + } + } + + public void reverseEachWord(StringBuilder sb) { + int n = sb.length(); + int start = 0, end = 0; + + while (start < n) { + // go to the end of the word + while (end < n && sb.charAt(end) != ' ') ++end; + // reverse the word + reverse(sb, start, end - 1); + // move to the next word + start = end + 1; + ++end; + } + } + + public String reverseWords(String s) { + // converst string to string builder + // and trim spaces at the same time + StringBuilder sb = trimSpaces(s); + + // reverse the whole string + reverse(sb, 0, sb.length() - 1); + + // reverse each word + reverseEachWord(sb); + + return sb.toString(); + } +} diff --git a/src/main/java/practiceproblems/RollingHashRabinKarp.java b/src/main/java/practiceproblems/RollingHashRabinKarp.java new file mode 100644 index 0000000..db20c8d --- /dev/null +++ b/src/main/java/practiceproblems/RollingHashRabinKarp.java @@ -0,0 +1,113 @@ +package practiceproblems; + +/** + * revise + */ +public class RollingHashRabinKarp{ + /** + * the normal way to calculate an hash from a string would be through the following method + * for String S="abcdef" we chose a prime number which is greater that the no.of chars(26) + * we choose P=31 + * + * total hash(abcdef)= (a*P^0+ b*P^1 + c*P^2 + d*P^3 + e*P^4 + f*P^5) % M; + * %(mod) is taken to avoid overflow of values + * hash at each point is [h0, h1, h2, h3, h4, h5] + * + * this is analogous to prefix sum, the hash at each index i is hash calculated from 0 to 'i' + * + * for h2 is hash from (a-c), in order to calculate prefix sum from index 2-4 + * + * sum(2-4)= sum(4)-sum(2) [sum 0-4 - sum 0-2], likewise we can calculate rolling hash + * + * in order to find contains subString for String size M and N, the time complexity would be O(MN) + * + * if we find the hash of String M and N separately and somehow find if substring of size N in String M + * hashes to hash(N) we will have our result in O(M) + * + * M= SDESKILLS + * N= SKILLS + * Note: p0 or p1= P^number + * + * hash of M= [(S*p0)+(D*p1)+(E*p2)+(S*p4)+(K*p5)+(I*p6)+(L*p7)+(L*p8)+(S*p9)] % M + * + * in hashing no matter how many times/ at what position a string comes, it has to hash to same value + * + * if we have a hash(abcdef)= a*p0+ b*p1+ c*p2+ d*p3+ e*p4+ f*p5 + * and we need hash(cde) we need to do prefix-sum analogy + * hash(cde)= hash(R)-hash(L-1) R= Right, L=Left + * but the above equation comes down to [c*p2 + d*p3+ e*p4] this is not going to yield correct + * result for cde every time if 'cde' occurs at different index then the eq would be [c*p8+d*p9+e*p10] + * + * so what we need is to add one more part to the eq ([c*p2 + d*p3+ e*p4]/ p2) => [c*p0 + d*p1+ e*p2] + * hash(cde)= hash(R)-hash(L-1)/P^L R= Right, L=Left + * + * Hash[3,6]= (Hash(6)- Hash(2)/ p^3)%M + * + * hash([L...R]) =(hash[R] - hash[L - 1]/PL)% M + hash(s[L...R]) =(hash[R] - hash[L - 1]) * P^-L % M + + hash(s[L...R]) =(hash[R] - hash[L - 1])*(P^-1)L % M + hash(s[L...R]) =(hash[R] - hash[L - 1])%M * (P-1)L%M + + (A * B) % M =( (A % M) * (B % M)) % M + X = P^-1= P^M-2% M + hash(s[L...R]) =(((hash[R] - hash[L - 1])% M *X^L )% M) + */ + + + private int prime = 101; + + public int patternSearch(char[] text, char[] pattern) { + int m = pattern.length; + int n = text.length; + long patternHash = createHash(pattern, m - 1); + long textHash = createHash(text, m - 1); + for (int i = 1; i <= n - m + 1; i++) { + if (patternHash == textHash && checkEqual(text, i - 1, i + m - 2, pattern, 0, m - 1)) { + return i - 1; + } + if (i < n - m + 1) { + textHash = recalculateHash(text, i - 1, i + m - 1, textHash, m); + } + } + return -1; + } + + private long recalculateHash(char[] str, int oldIndex, int newIndex, long oldHash, int patternLen) { + long newHash = oldHash - str[oldIndex]; + newHash = newHash / prime; + newHash += str[newIndex] * Math.pow(prime, patternLen - 1); + return newHash; + } + + private long createHash(char[] str, int end) { + long hash = 0; + for (int i = 0; i <= end; i++) { + hash += str[i] * Math.pow(prime, i); + } + return hash; + } + + private boolean checkEqual(char str1[], int start1, int end1, char str2[], int start2, int end2) { + if (end1 - start1 != end2 - start2) { + return false; + } + while (start1 <= end1 && start2 <= end2) { + if (str1[start1] != str2[start2]) { + return false; + } + start1++; + start2++; + } + return true; + } + + public static void main(String args[]) { + RollingHashRabinKarp rks = new RollingHashRabinKarp(); + System.out.println(rks.patternSearch("TusharRoy".toCharArray(), "sharRoy".toCharArray())); + System.out.println(rks.patternSearch("TusharRoy".toCharArray(), "Roy".toCharArray())); + System.out.println(rks.patternSearch("TusharRoy".toCharArray(), "shas".toCharArray())); + System.out.println(rks.patternSearch("TusharRoy".toCharArray(), "usha".toCharArray())); + System.out.println(rks.patternSearch("TusharRoy".toCharArray(), "Tus".toCharArray())); + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/RomanToInteger.java b/src/main/java/practiceproblems/RomanToInteger.java new file mode 100644 index 0000000..2ae1528 --- /dev/null +++ b/src/main/java/practiceproblems/RomanToInteger.java @@ -0,0 +1,53 @@ +package practiceproblems; + +import java.util.HashMap; +import java.util.Map; + +public class RomanToInteger { + + public int romanToInteger(String s) { + Map map = new HashMap<>(); + + 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); + + int sum = map.get(s.charAt(s.length() - 1)); + for (int i = s.length() - 2; i >= 0; --i) { + if (map.get(s.charAt(i)) < map.get(s.charAt(i + 1))) { + sum -= map.get(s.charAt(i)); + } else { + sum += map.get(s.charAt(i)); + } + } + return sum; + + } + + public String intToRoman(int num) { + + String[] keys = new String[]{"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"}; + int[] values = new int[]{1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}; + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < keys.length; i++) { + + if (num - values[i] >= 0) { + while (num - values[i] >= 0) { + sb.append(keys[i]); + num -= values[i]; + } + } + } + + return sb.toString(); + } + + public static void main(String[] args) { + new RomanToInteger().romanToInteger("LIX"); + } + +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/RotateArray.java b/src/main/java/practiceproblems/RotateArray.java new file mode 100644 index 0000000..d196ac0 --- /dev/null +++ b/src/main/java/practiceproblems/RotateArray.java @@ -0,0 +1,23 @@ +package practiceproblems; + +/** + * https://leetcode.com/problems/rotate-array/ + */ +public class RotateArray { + public void rotate(int[] nums, int k) { + k %= nums.length; + reverse(nums, 0, nums.length - 1); + reverse(nums, 0, k - 1); + reverse(nums, k, nums.length - 1); + } + + public void reverse(int[] nums, int start, int end) { + while (start < end) { + int temp = nums[start]; + nums[start] = nums[end]; + nums[end] = temp; + start++; + end--; + } + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/RotateMatrixInPlace.java b/src/main/java/practiceproblems/RotateMatrixInPlace.java new file mode 100644 index 0000000..e274210 --- /dev/null +++ b/src/main/java/practiceproblems/RotateMatrixInPlace.java @@ -0,0 +1,41 @@ +package practiceproblems; + +/** + * https://leetcode.com/problems/rotate-image/description/ + */ +class RotateMatrixInPlace { + /** + * For Swapping 90 -> transpose matrix (rows to col, col to rows) -> swap cols using 2 pointers + * For Swapping 180 -> swap columns using 2 pointer -> swap rows using 2 pointers + * For Swapping 270 -> transpose matrix (rows to col, col to rows) -> swap rows using 2 pointers + */ + public static void rotateAlter(int[][] matrix) { + + for (int i = 0; i < matrix.length; i++) { + //If we start j from 0, we would end up swapping each element twice. + // For example, we'd swap matrix[1][2] with matrix[2][1], + // and then later swap matrix[2][1] with matrix[1][2] + for (int j = i; j < matrix[0].length; j++) { + int temp = matrix[i][j]; + matrix[i][j] = matrix[j][i]; + matrix[j][i] = temp; + } + } + for (int i = 0; i < matrix.length; i++) { + for (int j = 0; j < matrix.length / 2; j++) { + int temp = matrix[i][j]; + matrix[i][j] = matrix[i][matrix.length - 1 - j]; + matrix[i][matrix.length - 1 - j] = temp; + } + } + } + + + public static void main(String[] args) { + int N = 4; + + // Test Case 1 + int[][] mat = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}, {13, 14, 15, 16}}; + rotateAlter(mat); + } +} \ No newline at end of file diff --git a/src/geeksforgeeks/SearchAMaze.java b/src/main/java/practiceproblems/SearchAMaze.java similarity index 50% rename from src/geeksforgeeks/SearchAMaze.java rename to src/main/java/practiceproblems/SearchAMaze.java index e810f2b..3b7f806 100644 --- a/src/geeksforgeeks/SearchAMaze.java +++ b/src/main/java/practiceproblems/SearchAMaze.java @@ -1,6 +1,6 @@ -package geeksforgeeks; +package practiceproblems; -import java.util.ArrayDeque; +import java.util.LinkedList; import java.util.Queue; /** @@ -19,36 +19,42 @@ private static boolean isValid(int mat[][], int row, int col) { } private static void BFS(int mat[][], int srcX, int srcY, int destX, int destY) { - Queue q = new ArrayDeque<>(); - q.add(new MazeNode(srcX, srcY, 0)); + Queue q = new LinkedList<>(); - int minDist = Integer.MAX_VALUE; + q.add(new MazeNode(srcX, srcY, 0)); + int result = 0; while (!q.isEmpty()) { - MazeNode node = q.poll(); - srcX = node.x; - srcY = node.y; - int dist = node.dist; + int size = q.size(); + for (int i = 0; i < size; i++) { - if (srcX == destX && srcY == destY) { - minDist = dist; - break; - } + MazeNode node = q.poll(); + + srcX = node.x; + srcY = node.y; + int dist = node.dist; + + if (srcX == destX && srcY == destY) { + System.out.print("The shortest path from source to destination " + "has length " + result); + return; + } - for (int k = 0; k < 4; k++) { - if (isValid(mat, srcX + row[k], srcY + col[k])) { - mat[srcX][srcY] = 0; - MazeNode e = new MazeNode(srcX + row[k], srcY + col[k], dist + 1); - q.add(e); - System.out.println(" Adding to the queue :" + e); + for (int k = 0; k < 4; k++) { + if (isValid(mat, srcX + row[k], srcY + col[k])) { + mat[srcX][srcY] = 0; + MazeNode e = new MazeNode(srcX + row[k], srcY + col[k], dist + 1); + q.add(e); + System.out.println("Adding to the queue :" + e); + } } } + result++; } - if (minDist != Integer.MAX_VALUE) { - System.out.print("The shortest path from source to destination " + "has length " + minDist); + if (result != Integer.MAX_VALUE) { + System.out.print("The shortest path from source to destination " + "has length " + result); } else { System.out.print("Destination can't be reached from source"); } @@ -62,18 +68,19 @@ public static void main(String[] args) { BFS(mat, 8, 0, 0, 9); } -} -class MazeNode { - int x, y, dist; + static class MazeNode { + int x, y, dist; - MazeNode(int x, int y, int dist) { - this.x = x; - this.y = y; - this.dist = dist; - } + MazeNode(int x, int y, int dist) { + this.x = x; + this.y = y; + this.dist = dist; + } - public String toString() { - return "(" + x + "," + y + ") -> " + dist; + public String toString() { + return "(" + x + "," + y + ") -> " + dist; + } } -}; \ No newline at end of file +} + diff --git a/src/main/java/practiceproblems/SetBitCount.java b/src/main/java/practiceproblems/SetBitCount.java new file mode 100644 index 0000000..e7872bd --- /dev/null +++ b/src/main/java/practiceproblems/SetBitCount.java @@ -0,0 +1,21 @@ +package practiceproblems; + +/** + * https://leetcode.com/problems/number-of-1-bits/discuss/55099/Simple-Java-Solution-Bit-Shifting + */ +public class SetBitCount { + public static int hammingWeight(int n) { + int count = 0; + + while (n != 0) { + n &= (n - 1); + count++; + } + + return count; + } + + public static void main(String[] args) { + System.out.println(hammingWeight(00000000000000000000000000001011)); + } +} diff --git a/src/main/java/practiceproblems/SetZeroesMatrix.java b/src/main/java/practiceproblems/SetZeroesMatrix.java new file mode 100644 index 0000000..12f8a1f --- /dev/null +++ b/src/main/java/practiceproblems/SetZeroesMatrix.java @@ -0,0 +1,74 @@ +package practiceproblems; + +import java.util.Arrays; + +/** + * https://leetcode.com/problems/set-matrix-zeroes + */ +public class SetZeroesMatrix { + + public void setZeroes(int[][] matrix) { + boolean firstRow = false; + boolean firstCol = false; + for (int i = 0; i < matrix.length; i++) { + for (int j = 0; j < matrix[0].length; j++) { + if (matrix[i][j] == 0) { + /** + * + // Use first row and first column as markers. + // if matrix[i][j] = 0, mark respected row and col marker = 0; indicating + that later this respective row and col must be marked 0; + // And because you are altering first row and column, + you need to have two variables to track their own status. + // So, for ex, if any one of the first row is 0, fr = 0, + and at the end set all first row to 0; + + */ + if (i == 0) firstCol = true; + if (j == 0) firstRow = true; + matrix[0][j] = 0; + matrix[i][0] = 0; + } + } + } + for (int i = 1; i < matrix.length; i++) { + for (int j = 1; j < matrix[0].length; j++) { + if (matrix[0][j] == 0 || matrix[i][0] == 0) { + matrix[i][j] = 0; + } + } + } + + if (firstRow) { + for (int i = 0; i < matrix.length; i++) { + matrix[i][0] = 0; + } + } + if (firstCol) { + Arrays.fill(matrix[0], 0); + } + } + + public void setZeroesExtraSpace(int[][] matrix){ + int[] row = new int[matrix.length]; + int[] col = new int[matrix[0].length]; + + for (int i = 0; i < matrix.length; i++) { + for (int j = 0; j < matrix[0].length; j++) { + if (matrix[i][j] == 0){ + row[i] = 1; + col[j] = 1; + } + } + } + + for (int i = 0; i < matrix.length; i++) { + for (int j = 0; j < matrix[0].length; j++) { + if (row[i] == 1 || col[j] == 1) { + matrix[i][j] = 0; + } + } + } + + } +} diff --git a/src/main/java/practiceproblems/ShortestPathBinaryMatrix.java b/src/main/java/practiceproblems/ShortestPathBinaryMatrix.java new file mode 100644 index 0000000..3458297 --- /dev/null +++ b/src/main/java/practiceproblems/ShortestPathBinaryMatrix.java @@ -0,0 +1,51 @@ +package practiceproblems; + +import java.util.ArrayDeque; +import java.util.Deque; + +/** + * https://leetcode.com/problems/shortest-path-in-binary-matrix/ + */ +public class ShortestPathBinaryMatrix { + public int shortestPathBinaryMatrix(int[][] grid) { + + int m = grid.length; + int n = grid[0].length; + + if (grid[0][0] == 1 || grid[m - 1][n - 1] == 1) { + return -1; + } + int[][] dirs = new int[][]{{0, 1}, {1, 0}, {0, -1}, {-1, 0}, {1, 1}, {1, -1}, {-1, 1}, {-1, -1}}; + Deque queue = new ArrayDeque<>(); + queue.offer(new int[]{0, 0}); + boolean[][] visited = new boolean[grid.length][grid[0].length]; + visited[0][0] = true; + int steps = 0; + while (!queue.isEmpty()) { + int size = queue.size(); + + for (int i = 0; i < size; i++) { + + int[] temp = queue.poll(); + if (temp[0] == grid.length - 1 && temp[1] == grid[0].length - 1) return steps + 1; + for (int[] dir : dirs) { + + int newX = temp[0] + dir[0]; + int newY = temp[1] + dir[1]; + + if (newX < 0 || newY < 0 || newX >= grid.length || newY >= grid[0].length || visited[newX][newY] || grid[newX][newY] == 1) { + continue; + } + + visited[newX][newY] = true; + + queue.offer(new int[]{newX, newY}); + } + } + + steps++; + } + + return -1; + } +} diff --git a/src/main/java/practiceproblems/ShuffleArray.java b/src/main/java/practiceproblems/ShuffleArray.java new file mode 100644 index 0000000..9552360 --- /dev/null +++ b/src/main/java/practiceproblems/ShuffleArray.java @@ -0,0 +1,51 @@ +package practiceproblems; + +import java.util.Arrays; +import java.util.Random; + +/** + * https://leetcode.com/problems/shuffle-an-array/ + */ +public class ShuffleArray { + + private int[] nums; + private Random random; + + public ShuffleArray(int[] nums) { + this.nums = nums; + random = new Random(); + } + + /** + * Resets the array to its original configuration and return it. + */ + public int[] reset() { + return nums; + } + + /** + * Returns a random shuffling of the array. + */ + public int[] shuffle() { + + int[] a = Arrays.copyOfRange(nums, 0, nums.length); + for (int j = 1; j < a.length; j++) { + int i = random.nextInt(j + 1); + swap(a, i, j); + } + return a; + } + + private void swap(int[] a, int i, int j) { + int t = a[i]; + a[i] = a[j]; + a[j] = t; + } + + public static void main(String[] args) { + int[] arr = { 1, 2, 3 }; + ShuffleArray sa = new ShuffleArray(arr); + System.out.println("After shuffling :" + Arrays.toString(sa.shuffle())); + System.out.println("Reset : " + Arrays.toString(sa.reset())); + } +} diff --git a/src/main/java/practiceproblems/SimilarExpressions.java b/src/main/java/practiceproblems/SimilarExpressions.java new file mode 100644 index 0000000..b224769 --- /dev/null +++ b/src/main/java/practiceproblems/SimilarExpressions.java @@ -0,0 +1,55 @@ +package practiceproblems; + +/** + * https://www.geeksforgeeks.org/check-two-expressions-brackets/ + */ +//unresolved +public class SimilarExpressions { + + public static void main(String[] args) { + + System.out.println('+' * '-'); + + String query = "-(a+b+c)"; + String response = "-a-b-c"; + + checkExpression(query.toCharArray(), response.toCharArray()); + } + + private static void checkExpression(char[] query, char[] response) { + + char symbol = 0; + int startIndex = 0; + int endIndex = 0; + for (int i = 0; i < query.length; i++) { + if (query[i] == '(') { + symbol = query[i - 1]; + startIndex = i + 1; + } else if (query[i] == ')') { + endIndex = i - 1; + solve(symbol, startIndex, endIndex, query); + + } + } + } + + private static void solve(char symbol, int startIndex, int endIndex, char[] query) { + + for (int i = startIndex; i <= endIndex; i++) { + char temp = 0; + if (query[startIndex] == '-') { + temp = '-' * '+'; + query[i] = temp; + } else if (symbol == '+') { + temp = '-'; + query[i] = temp; + } else if (Character.isLetter(query[startIndex])) { + if (startIndex == i) { + query[startIndex - 1] = temp; + } + System.out.print(query[startIndex]); + } + System.out.print(temp); + } + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/SlidingWindow.java b/src/main/java/practiceproblems/SlidingWindow.java new file mode 100644 index 0000000..7995bc5 --- /dev/null +++ b/src/main/java/practiceproblems/SlidingWindow.java @@ -0,0 +1,46 @@ +package practiceproblems; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.List; + +/** + * https://leetcode.com/problems/sliding-window-maximum/ + */ +public class SlidingWindow { + + public static void main(String[] args) { + int arr[] = {8, 5, 10, 7, 9, 4, 15, 12, 90, 13}; + int k = 3; + maxSlidingWindow(arr, k); + } + + public static int[] maxSlidingWindow(int[] nums, int k) { + + List result = new ArrayList<>(); + + Deque queue = new ArrayDeque<>(); + + for (int i = 0; i < nums.length; i++) { + while (!queue.isEmpty() && queue.peek() < i - k + 1) { + queue.removeFirst(); + } + while (!queue.isEmpty() && nums[queue.peekLast()] < nums[i]) { + queue.pollLast(); + } + + queue.addLast(i); + + if (i >= k - 1) { + result.add(nums[queue.peekFirst()]); + } + } + int[] res = new int[nums.length - k + 1]; + + for (int j = 0; j < result.size(); j++) { + res[j] = result.get(j); + } + return res; + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/SnakeAndLadder.java b/src/main/java/practiceproblems/SnakeAndLadder.java new file mode 100644 index 0000000..eea9c28 --- /dev/null +++ b/src/main/java/practiceproblems/SnakeAndLadder.java @@ -0,0 +1,70 @@ +package practiceproblems; + +import java.util.LinkedList; +import java.util.Queue; + +/** + * tricky bfs, consider as 1-D plane + * https://leetcode.com/problems/snakes-and-ladders/ + */ +public class SnakeAndLadder { + + public int snakesAndLadders(int[][] board) { + int n = board.length; + Queue queue = new LinkedList<>(); + queue.offer(1); + boolean[] visited = new boolean[n * n + 1]; + for (int move = 0; !queue.isEmpty(); move++) { + + for (int size = queue.size(); size > 0; size--) { + int currPos = queue.poll(); + if (visited[currPos]) continue; + visited[currPos] = true; + if (currPos == n * n) return move; + for (int i = 1; i <= 6 && currPos + i <= n * n; i++) { + int next = currPos + i; + int value = getBoardValue(board, next); + if (value > 0) next = value; + if (!visited[next]) queue.offer(next); + } + } + } + return -1; + } + + /** + * Let nextPos=1 + * 1/6=0 + * 6-0-1=5 <-- this is the row we are at + * This will work for 1,2,3,4,5 but not for 6 + * 6/6=1 + * 6-1-1=4, but we are still at row 5 + * So the way to tackle this is by using this: + * int oldRow=(next_step-1)/n; + * row=n-1-oldRow; + * This will make 1,2,3,4,5,6 all in the same row. + * column is easy all u have to do is int oldCol=(next_step-1)%n; + * 2. For flipping direction (i.e snake goes upward in zig-zag fashion )all we are going to dow is we have found oldRow in the previous step. + * so for every odd oldRow we flip the direction and for every even oldRow we maintain our normal direction. + *

+ * if(x%2==1) col =n-1-oldCol; + */ + private int getBoardValue(int[][] board, int nextPos) { + int n = board.length; + int oldRow = (nextPos - 1) / n; + int row = n - 1 - oldRow; + int oldCol = (nextPos - 1) % n; + int col = oldRow % 2 == 0 ? oldCol : n - 1 - oldCol; + + return board[row][col]; + } + + public static void main(String[] args) { + + int[][] 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}}; + + System.out.println("Min Dice throws required is " + new SnakeAndLadder().snakesAndLadders(board)); + + } +} \ No newline at end of file diff --git a/src/geeksforgeeks/SortANearlySortedArray.java b/src/main/java/practiceproblems/SortANearlySortedArray.java similarity index 90% rename from src/geeksforgeeks/SortANearlySortedArray.java rename to src/main/java/practiceproblems/SortANearlySortedArray.java index 0924886..78b3f04 100644 --- a/src/geeksforgeeks/SortANearlySortedArray.java +++ b/src/main/java/practiceproblems/SortANearlySortedArray.java @@ -1,11 +1,16 @@ -package geeksforgeeks; +package practiceproblems; import java.util.Arrays; -import java.util.Comparator; import java.util.List; import java.util.PriorityQueue; -/*https://www.techiedelight.com/sort-k-sorted-array/*/ +/** + * https://www.techiedelight.com/sort-k-sorted-array/ + * + * tricky priority queue + */ + + class SortANearlySortedArray { public static void sortKSortedArray(List list, int k) { diff --git a/src/main/java/practiceproblems/SortedSquares.java b/src/main/java/practiceproblems/SortedSquares.java new file mode 100644 index 0000000..f537fb9 --- /dev/null +++ b/src/main/java/practiceproblems/SortedSquares.java @@ -0,0 +1,34 @@ +package practiceproblems; + +/** + * https://leetcode.com/problems/squares-of-a-sorted-array + */ +public class SortedSquares { + // Input: [-7,-3,2,3,11] + // Output: [4,9,9,49,121] + public int[] sortedSquares(int[] nums) { + + int[] result = new int[nums.length]; + + int i = 0; + int j = nums.length - 1; + int k = nums.length - 1; + while (i <= j) { + + if (nums[i] * nums[i] < nums[j] * nums[j]) { + result[k--] = nums[j] * nums[j]; + j--; + } else if (nums[i] * nums[i] > nums[j] * nums[j]) { + result[k--] = nums[i] * nums[i]; + i++; + } else { + result[k--] = nums[i] * nums[i]; + if (k >= 0) result[k--] = nums[i] * nums[i]; + i++; + j--; + } + } + + return result; + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/SpiralMatrix.java b/src/main/java/practiceproblems/SpiralMatrix.java new file mode 100644 index 0000000..43f0fd4 --- /dev/null +++ b/src/main/java/practiceproblems/SpiralMatrix.java @@ -0,0 +1,234 @@ +package practiceproblems; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * tricky matrix + */ +class SpiralMatrix { + + public static List spiralOrder(int[][] matrix) { + + List res = new ArrayList<>(); + + if (matrix.length == 0) { + return res; + } + + int rowBegin = 0; + int rowEnd = matrix.length - 1; + int colBegin = 0; + int colEnd = matrix[0].length - 1; + + while (rowBegin <= rowEnd && colBegin <= colEnd) { + // Traverse Right + for (int j = colBegin; j <= colEnd; j++) { + System.out.println(" Right " + matrix[rowBegin][j]); + res.add(matrix[rowBegin][j]); + } + rowBegin++; + + // Traverse Down + for (int j = rowBegin; j <= rowEnd; j++) { + System.out.println(" Down " + matrix[j][colEnd]); + res.add(matrix[j][colEnd]); + } + colEnd--; + + // Make sure we are now on a different row. + if (rowBegin <= rowEnd) { // without this condition, this corner test case [[2,3]] would print [2,3,2] + // Traverse Left + for (int j = colEnd; j >= colBegin; j--) { + System.out.println(" Left " + matrix[rowEnd][j]); + res.add(matrix[rowEnd][j]); + } + } + rowEnd--; + + // It's necessary because after we've traversed right, down, and left, + // we might have exhausted all columns. + + if (colBegin <= colEnd) { + // Travel Up + // this block's work is to lift 1 row up from bottom + // the rest of the work will be done by first 'Right' loop again + for (int j = rowEnd; j >= rowBegin; j--) { + System.out.println(" uppp " + matrix[j][colBegin]); + res.add(matrix[j][colBegin]); + } + } + colBegin++; + } + + return res; + } + + public static void main(String[] args) { + +// int a[][] = {{1, 2, 3, 4}, +// {5, 6, 7, 8}, +// {9, 10, 11, 12}, +// {13, 14, 15, 16}}; +// spiralOrder(a); + + SpiralMatrix sm = new SpiralMatrix(); + sm.spiralMatrixIII(5, 6, 1, 4); + } + + public int[][] generateMatrix(int n) { + + int[][] matrix = new int[n][n]; + + int rowBegin = 0; + int rowEnd = n - 1; + int colBegin = 0; + int colEnd = n - 1; + int num = 1; + while (rowBegin <= rowEnd && colBegin <= colEnd) { + // Traverse Right + for (int j = colBegin; j <= colEnd; j++) { + matrix[rowBegin][j] = num++; + } + rowBegin++; + + // Traverse Down + for (int j = rowBegin; j <= rowEnd; j++) { + matrix[j][colEnd] = num++; + } + colEnd--; + + // Make sure we are now on a different row. + if (rowBegin <= rowEnd) { // without this condition, this corner test case [[2,3]] would print [2,3,2] + // Traverse Left + for (int j = colEnd; j >= colBegin; j--) { + matrix[rowEnd][j] = num++; + } + } + rowEnd--; + + // It's necessary because after we've traversed right, down, and left, + // we might have exhausted all columns. + + if (colBegin <= colEnd) { + // Travel Up + // this block's work is to lift 1 row up from bottom + // the rest of the work will be done by first 'Right' loop again + for (int j = rowEnd; j >= rowBegin; j--) { + matrix[j][colBegin] = num++; + } + } + colBegin++; + } + + return matrix; + } + + //The idea here is that once we start at (r=r0, c=c0), + // we walk along the east, then south, then west, and then north. + public int[][] spiralMatrixIII(int R, int C, int r0, int c0) { + int[][] dirt = new int[][]{{0, 1}, {1, 0}, {0, -1}, {-1, 0}}; // east, south, west, north is 0, 1, 2, + // 3 + int[][] res = new int[R * C][2]; + int len = 0, d = 0, j = 0; // move steps in the direction + res[j++] = new int[]{r0, c0}; + //After starting at (r0,c0), we need to walk in spirals, + // where the length of the spiral increases after every two directions. + // For example 2, we start at (r0=1, c0=4), then we go east by one length, we go south by one length. + // Following that, we go west by 2 length and then, go north by 2 length. + // After that, we go in next directions by 3 lengths, and so on. + while (j < R * C) { // fill all the blanks + if (d == 0 || d == 2) { + len++; // when move east or west, the length of path need plus 1 + } + for (int i = 0; i < len; i++) { + r0 += dirt[d][0]; + c0 += dirt[d][1]; + if (r0 >= 0 && r0 < R && c0 >= 0 && c0 < C) // check valid + res[j++] = new int[]{r0, c0}; + } + d = (d + 1) % 4; // turn to next direction + } + return res; + } + + public int[][] spiralMatrixWithLinkedList(int m, int n, ListNode head) { + int[][] matrix = new int[m][n]; + + for (int i = 0; i < m; i++) { + Arrays.fill(matrix[i], -1); + } + int rowBegin = 0; + int rowEnd = m - 1; + int colBegin = 0; + int colEnd = n - 1; + + while (rowBegin <= rowEnd && colBegin <= colEnd) { + + for (int j = colBegin; j <= colEnd; j++) { + if (head != null) { + matrix[rowBegin][j] = head.val; + head = head.next; + } + } + rowBegin++; + + for (int j = rowBegin; j <= rowEnd; j++) { + if (head != null) { + matrix[j][colEnd] = head.val; + head = head.next; + } + } + colEnd--; + + // Make sure we are now on a different row. + if (rowBegin <= rowEnd) { // without this condition, this corner test case [[2,3]] would print [2,3,2] + for (int j = colEnd; j >= colBegin; j--) { + if (head != null) { + matrix[rowEnd][j] = head.val; + head = head.next; + } + } + } + rowEnd--; + + // It's necessary because after we've traversed right, down, and left, + // we might have exhausted all columns. + + if (colBegin <= colEnd) { + // Travel Up + // this block's work is to lift 1 row up from bottom + // the rest of the work will be done by first 'Right' loop again + for (int j = rowEnd; j >= rowBegin; j--) { + if (head != null) { + matrix[j][colBegin] = head.val; + head = head.next; + } + } + } + colBegin++; + } + + return matrix; + } + + class ListNode { + int val; + ListNode next; + + ListNode() { + } + + ListNode(int val) { + this.val = val; + } + + ListNode(int val, ListNode next) { + this.val = val; + this.next = next; + } + } + +} + diff --git a/src/main/java/practiceproblems/StringJustify.java b/src/main/java/practiceproblems/StringJustify.java new file mode 100644 index 0000000..acde24f --- /dev/null +++ b/src/main/java/practiceproblems/StringJustify.java @@ -0,0 +1,99 @@ +package practiceproblems; + +import java.util.ArrayList; +import java.util.List; + +/** + * https://leetcode.com/problems/text-justification + */ +public class StringJustify { + public List fullJustify(String[] words, int maxWidth) { + List result = new ArrayList<>(); + + int start = 0; + int end = 0; + while (start < words.length) { + end = findLastWordIndex(words, start, maxWidth); + + String line = justify(words, start, end, maxWidth); + result.add(line); + + start = end + 1; + } + + return result; + } + + private int findLastWordIndex(String[] words, int i, int maxWidth) { + int j = i; + + int currWidth = words[j].length(); //took first word at j, notice first one doesn't need a space. + j++; + // +1 is considered as space between the words + while (j < words.length && (currWidth + 1 + words[j].length() <= maxWidth)) { + currWidth = currWidth + 1 + words[j].length(); + j++; + } + + return j - 1; //end now points at the next index so -1 + } + + private String justify(String[] words, int i, int j, int maxWidth) { + // if there is only one word possible in this window, we simply pad extra spaces. + if (j - i == 0) return padResult(words[i], maxWidth); + + // For last line, j will always point to the last element. + boolean isLastLine = j == words.length - 1; + + // find the length of words. + int l = 0; + for (int k = i; k <= j; k++) { + l += words[k].length(); + } + + int numSpaces = maxWidth - l; + int numWordsToPad = j - i; // for total of 3 words, j=2, i=0 so 2 words to pad (since we don't pad last one) + + StringBuilder sb = new StringBuilder(); + + // SpaceStr is the string with right num of spaces that should be attached to each word (numWordsToPad) + String spaceStr = isLastLine ? " " : blank(numSpaces / numWordsToPad); // simple separation in last line + // remainderSpaceCount is the number of extra space that need to be attached to first words (from left) + // if we need to add 5 space and, we have 3 words we need to add extra space in round-robin manner + int remainderSpaceCount = isLastLine ? 0 : numSpaces % numWordsToPad; + + for (int k = i; k <= j; k++) { + sb.append(words[k]).append(spaceStr); // notice we also end up attaching to the last word, we will trim it later + + // also append extra spaces + if (remainderSpaceCount > 0) { + sb.append(" "); + remainderSpaceCount--; + } + } + + String line = sb.toString().trim(); // the last word will also have spaces, so need to tirm. + + return padResult(line, maxWidth); // if the last word still needs to be padded. + } + + private String padResult(String result, int maxWidth) { + return result + blank(maxWidth - result.length()); + } + + private String blank(int count) { + StringBuilder sb = new StringBuilder(); + while (count > 0) { + sb.append(" "); + count--; + } + + return sb.toString(); + } + + public static void main(String[] args) { + // System.out.println(new StringJustify().fullJustify(new String[]{"This", "is", "an", "example", "of", "text", "justification."}, 16)); + System.out.println(new StringJustify().fullJustify(new String[]{"What","must","be","acknowledgment","shall","be"}, 16)); + System.out.println(new StringJustify().fullJustify(new String[]{"Science","is","what","we","understand","well","enough","to","explain","to","a","computer.","Art","is","everything","else","we","do"}, 20)); + } +} diff --git a/src/geeksforgeeks/SubstringWindowTemplate.java b/src/main/java/practiceproblems/SubstringWindowTemplate.java similarity index 98% rename from src/geeksforgeeks/SubstringWindowTemplate.java rename to src/main/java/practiceproblems/SubstringWindowTemplate.java index 9ba395d..abc7a81 100644 --- a/src/geeksforgeeks/SubstringWindowTemplate.java +++ b/src/main/java/practiceproblems/SubstringWindowTemplate.java @@ -1,4 +1,4 @@ -package geeksforgeeks; +package practiceproblems; import java.util.HashMap; import java.util.LinkedList; diff --git a/src/main/java/practiceproblems/SumOfSquares.java b/src/main/java/practiceproblems/SumOfSquares.java new file mode 100644 index 0000000..5f0b272 --- /dev/null +++ b/src/main/java/practiceproblems/SumOfSquares.java @@ -0,0 +1,19 @@ +package practiceproblems; + +/** + * https://leetcode.com/problems/sum-of-square-numbers/ + * + * Given a non-negative integer c, decide whether there're two integers a and b such that a2 + b2 = c. + */ +public class SumOfSquares { + public boolean judgeSquareSum(int c) { + long l = 0, r = (long) Math.sqrt(c); + while (l <= r) { // this <= other than < + long sum = l * l + r * r; + if (sum == c) return true; + if (sum < c) l++; + else r--; + } + return false; + } +} diff --git a/src/geeksforgeeks/SumSubArrayZero.java b/src/main/java/practiceproblems/SumSubArrayZero.java similarity index 73% rename from src/geeksforgeeks/SumSubArrayZero.java rename to src/main/java/practiceproblems/SumSubArrayZero.java index f8108de..11fed3f 100644 --- a/src/geeksforgeeks/SumSubArrayZero.java +++ b/src/main/java/practiceproblems/SumSubArrayZero.java @@ -1,4 +1,4 @@ -package geeksforgeeks; +package practiceproblems; import java.util.ArrayList; import java.util.HashMap; @@ -7,22 +7,26 @@ /** * https://www.geeksforgeeks.org/print-all-subarrays-with-0-sum/ + * + * tricky sub array sum */ -class Pair { - int first, second; - Pair(int a, int b) { - first = a; - second = b; - } - - public String toString() { - return this.first + "--" + this.second; - } -} public class SumSubArrayZero { + private static class Pair { + int first, second; + + Pair(int a, int b) { + first = a; + second = b; + } + + public String toString() { + return this.first + "--" + this.second; + } + } + static ArrayList findSubArrays(int[] arr, int n) { Map> map = new HashMap<>(); ArrayList result = new ArrayList<>(); @@ -34,8 +38,8 @@ static ArrayList findSubArrays(int[] arr, int n) { List al = new ArrayList<>(); if (map.containsKey(sum)) { al = map.get(sum); - for (int it = 0; it < al.size(); it++) { - result.add(new Pair(al.get(it) + 1, i)); + for (Integer integer : al) { + result.add(new Pair(integer + 1, i)); } } al.add(i); @@ -50,15 +54,14 @@ public static void main(String args[]) { ArrayList out = findSubArrays(arr, n); - if (out.size() == 0) + if (out.isEmpty()) System.out.println("No subarray exists"); else print(out); } static void print(ArrayList out) { - for (int i = 0; i < out.size(); i++) { - Pair p = out.get(i); + for (Pair p : out) { System.out.println("Subarray found from Index " + p.first + " to " + p.second); } } diff --git a/src/main/java/practiceproblems/SurroundedRegions.java b/src/main/java/practiceproblems/SurroundedRegions.java new file mode 100644 index 0000000..4528267 --- /dev/null +++ b/src/main/java/practiceproblems/SurroundedRegions.java @@ -0,0 +1,138 @@ +package practiceproblems; + +import java.util.ArrayDeque; +import java.util.Queue; + +/** + * Given a 2D board containing 'X' and 'O' (the letter O), + * capture all regions surrounded by 'X'. + *

+ * A region is captured by flipping all 'O's into 'X's in that surrounded region. + *

+ * Example: + * X X X X + * X O O X + * X X O X + * X O X X + * After running your function, the board should be: + * X X X X + * X X X X + * X X X X + * X O X X + * Surrounded regions shouldn’t be on the border, + * which means that any 'O' on the border of the board are not flipped to 'X'. + * Any 'O' that is not on the border and + * it is not connected to an 'O' on the border will be flipped to 'X'. + * Two cells are connected if they are adjacent cells connected horizontally or vertically. + */ +public class SurroundedRegions { + private static class Pair { + int x; + int y; + int level; + + public Pair(int x, int y, int level) { + this.x = x; + this.y = y; + this.level = level; + } + } + + public void solve(char[][] board) { + if (board == null || board.length == 0) + return; + + Queue queue = new ArrayDeque<>(); + int[][] dirs = {{-1, 0}, {1, 0}, {0, 1}, {0, -1}}; + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[0].length; j++) { + if (board[i][j] == 'O') { + if (i == board.length - 1 || j == board[0].length - 1 || i == 0 || j == 0) { + board[i][j] = '1'; + queue.offer(new Pair(i, j, 0)); + } + } + } + } + + while (!queue.isEmpty()) { + Pair temp = queue.poll(); + + for (int[] dir : dirs) { + int newx = temp.x + dir[0]; + int newy = temp.y + dir[1]; + + if (isValid(newx, newy, board) && board[newx][newy] == 'O') { + board[newx][newy] = '1'; + queue.offer(new Pair(newx, newy, 0)); + } + } + + } + + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[0].length; j++) { + if (board[i][j] == '1') { + board[i][j] = 'O'; + } else if (board[i][j] == 'O') { + board[i][j] = 'X'; + } + } + } + + } + + public boolean isValid(int x, int y, char[][] board) { + return x >= 0 && x < board.length && y >= 0 && y < board[0].length; + } + + public void solveDFS(char[][] board) { + if (board.length == 0 || board[0].length == 0) + return; + if (board.length < 2 || board[0].length < 2) + return; + int m = board.length, n = board[0].length; + // Any 'O' connected to a boundary can't be turned to 'X', so ... + // Start from first and last column, turn 'O' to '*'. + for (int i = 0; i < m; i++) { + if (board[i][0] == 'O') + boundaryDFS(board, i, 0); + if (board[i][n - 1] == 'O') + boundaryDFS(board, i, n - 1); + } + // Start from first and last row, turn '0' to '*' + for (int j = 0; j < n; j++) { + if (board[0][j] == 'O') + boundaryDFS(board, 0, j); + if (board[m - 1][j] == 'O') + boundaryDFS(board, m - 1, j); + } + // post-processing, turn 'O' to 'X', '*' back to 'O', keep 'X' intact. + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (board[i][j] == 'O') + board[i][j] = 'X'; + else if (board[i][j] == '*') + board[i][j] = 'O'; + } + } + } + + //Use DFS algo to turn internal however boundary-connected 'O' to '*' + private void boundaryDFS(char[][] board, int i, int j) { + if (i < 0 || i > board.length - 1 || j < 0 || j > board[0].length - 1) + return; + if (board[i][j] == 'O') { + board[i][j] = '*'; + + boundaryDFS(board, i - 1, j); + + boundaryDFS(board, i + 1, j); + + boundaryDFS(board, i, j - 1); + + boundaryDFS(board, i, j + 1); + } + } +} + diff --git a/src/main/java/practiceproblems/TaskLeastInterval.java b/src/main/java/practiceproblems/TaskLeastInterval.java new file mode 100644 index 0000000..100906a --- /dev/null +++ b/src/main/java/practiceproblems/TaskLeastInterval.java @@ -0,0 +1,77 @@ +package practiceproblems; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.PriorityQueue; + +/** + * https://leetcode.com/problems/task-scheduler/ + * + * tricky priority queue + */ + + +public class TaskLeastInterval { + + public static int leastInterval(char[] tasks, int n) { + Map cache = new HashMap<>(); + + for (char c : tasks) { + cache.put(c, cache.getOrDefault(c, 0) + 1); + } + + PriorityQueue queue = new PriorityQueue<>((a, b) -> Integer.compare(b.freq, a.freq)); + + for (char key : cache.keySet()) { + queue.offer(new Pair(key, cache.get(key))); + } + int result = 0; + // At each iteration, we process at most 'n' elements, + // and move forwards exactly n+1 in time (regardless of how many elements we processed: + // Read the topmost from the queue and increment the time. Add it to a temp list to be added later. + // Add the element back to the queue from the temp list if count is > 0. + // if al elements are done, we're done too. + // Move time forward by n + 1 + // Return time in the end. + while (!queue.isEmpty()) { + int k = n + 1; //n slots for the gap and 1 for the task itself, At each iteration, we process at most 'n' elements, + // and move forwards exactly n+1 in time + List tempList = new ArrayList<>(); + + while (k > 0 && !queue.isEmpty()) { + Pair temp = queue.poll(); // most frequency task + temp.freq -= 1; // decrease frequency, meaning it got executed + tempList.add(temp); // collect task to add back to queue + result++; //successfully executed task + k--; + } + + for (Pair t : tempList) { + if (t.freq > 0) queue.offer(t); // add valid tasks + } + + if (queue.isEmpty()) break; + + result += k; // if k > 0, then it means we need to be idle + + } + return result; + } + + public static void main(String[] args) { + char[] arr = "A".toCharArray(); + System.out.println(leastInterval(arr, 2)); + } + + static class Pair { + char task; + int freq; + + public Pair(char task, int freq) { + this.task = task; + this.freq = freq; + } + } +} diff --git a/src/main/java/practiceproblems/ThreeSum.java b/src/main/java/practiceproblems/ThreeSum.java new file mode 100644 index 0000000..e0fef3c --- /dev/null +++ b/src/main/java/practiceproblems/ThreeSum.java @@ -0,0 +1,68 @@ +package practiceproblems; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class ThreeSum { + public List> threeSum(int[] nums) { + if (nums == null || nums.length == 0) return Collections.emptyList(); + + Arrays.sort(nums); + List> result = new ArrayList<>(); + for (int i = 0; i < nums.length; i++) { + if (i == 0 || (nums[i] != nums[i - 1])) { + + int left = i + 1; + int right = nums.length - 1; + int sum = -nums[i]; + while (left < right) { + if (nums[left] + nums[right] == sum) { + + result.add(Arrays.asList(nums[i], nums[left], nums[right])); + left++; + right--; + while (left < right && nums[left] == nums[left - 1]) left++; + while (left < right && nums[right] == nums[right + 1]) right--; + } else if (nums[left] + nums[right] < sum) { + left++; + } else if (nums[left] + nums[right] > sum) { + right--; + } + } + } + } + + return result; + } + + public int threeSumClosest(int[] nums, int target) { + + Arrays.sort(nums); + int initialSum = nums[0] + nums[1] + nums[nums.length - 1]; + for (int i = 0; i < nums.length - 2; i++) { + + int j = i + 1; + int k = nums.length - 1; + + while (j < k) { + int sum = nums[i] + nums[j] + nums[k]; + + if (sum > target) { + k--; + while (j < k && nums[k] == nums[k + 1]) k--; + } else { + j++; + while (j < k && nums[k] == nums[j - 1]) j++; + } + + if (Math.abs(target - sum) < Math.abs(target - initialSum)) { + initialSum = sum; + } + } + } + + return initialSum; + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/TopKFrequentElements.java b/src/main/java/practiceproblems/TopKFrequentElements.java new file mode 100644 index 0000000..a595729 --- /dev/null +++ b/src/main/java/practiceproblems/TopKFrequentElements.java @@ -0,0 +1,65 @@ +package practiceproblems; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.PriorityQueue; + +/** + * https://leetcode.com/problems/top-k-frequent-elements/discuss/445815/Java8-Lambda-solution-3-lines-code + */ +public class TopKFrequentElements { + + public Integer[] topKFrequentBucket(int[] nums, int k) { + + List[] bucket = new ArrayList[nums.length + 1]; + Map frequencyMap = new HashMap<>(); + + for (int n : nums) { + frequencyMap.put(n, frequencyMap.getOrDefault(n, 0) + 1); + } + + for (int key : frequencyMap.keySet()) { + int frequency = frequencyMap.get(key); + if (bucket[frequency] == null) { + bucket[frequency] = new ArrayList<>(); + } + bucket[frequency].add(key); + } + + List res = new ArrayList<>(); + + for (int pos = bucket.length - 1; pos >= 0 && res.size() < k; pos--) { + if (bucket[pos] != null) { + res.addAll(bucket[pos]); + } + } + return res.toArray(new Integer[k]); + } + + public List topKFrequent(int[] nums, int k) { + if (nums.length == 0) { + return Collections.emptyList(); + } + + PriorityQueue> pq = new PriorityQueue<>( + (obj1, obj2) -> obj2.getValue() - obj1.getValue()); + Map map = new HashMap<>(); + + for (int i : nums) { + map.put(i, map.getOrDefault(i, 0) + 1); + } + + for (Map.Entry mapEntry : map.entrySet()) { + pq.add(mapEntry); + } + + List result = new ArrayList<>(); + for (int i = 0; i < k; i++) { + result.add(pq.remove().getKey()); + } + return result; + } +} diff --git a/src/geeksforgeeks/TrailingZeroes.java b/src/main/java/practiceproblems/TrailingZeroes.java similarity index 94% rename from src/geeksforgeeks/TrailingZeroes.java rename to src/main/java/practiceproblems/TrailingZeroes.java index d77577e..6494c51 100644 --- a/src/geeksforgeeks/TrailingZeroes.java +++ b/src/main/java/practiceproblems/TrailingZeroes.java @@ -1,9 +1,8 @@ -package geeksforgeeks; +package practiceproblems; /*https://www.geeksforgeeks.org/count-trailing-zeroes-factorial-number/*/ class TrailingZeroes { public int trailingZeroes(int n) { - int count = 0; while (n != 0) { int tmp = n / 5; diff --git a/src/geeksforgeeks/TreasureIsland.java b/src/main/java/practiceproblems/TreasureIsland.java similarity index 50% rename from src/geeksforgeeks/TreasureIsland.java rename to src/main/java/practiceproblems/TreasureIsland.java index 1230d8f..95da6c0 100644 --- a/src/geeksforgeeks/TreasureIsland.java +++ b/src/main/java/practiceproblems/TreasureIsland.java @@ -1,30 +1,31 @@ -package geeksforgeeks; +package practiceproblems; import java.util.LinkedList; import java.util.Queue; -// https://leetcode.com/discuss/interview-question/347457/Amazon-or-OA-2019-or-Treasure-Island +/** + * https://leetcode.com/discuss/interview-question/347457/Amazon-or-OA-2019-or-Treasure-Island + */ public class TreasureIsland { - private static final int[][] DIRS = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}}; + private static final int[][] DIRS = { { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 } }; public static int minSteps(char[][] grid) { Queue q = new LinkedList<>(); - q.add(new Point(0, 0)); - grid[0][0] = 'D'; // mark as visited - for (int steps = 1; !q.isEmpty(); steps++) { - for (int sz = q.size(); sz > 0; sz--) { - Point p = q.poll(); - - for (int[] dir : DIRS) { - int r = p.r + dir[0]; - int c = p.c + dir[1]; - - if (isSafe(grid, r, c)) { - if (grid[r][c] == 'X') - return steps; - grid[r][c] = 'D'; - q.add(new Point(r, c)); + q.add(new Point(0, 0, 0)); + grid[0][0] = 'D'; + while (!q.isEmpty()) { + Point p = q.poll(); + + for (int[] dir : DIRS) { + int r = p.r + dir[0]; + int c = p.c + dir[1]; + + if (isSafe(grid, r, c)) { + if (grid[r][c] == 'X') { + return p.steps + 1; } + grid[r][c] = 'D'; + q.add(new Point(r, c, p.steps + 1)); } } } @@ -36,11 +37,14 @@ private static boolean isSafe(char[][] grid, int r, int c) { } private static class Point { - int r, c; + int r; + int c; + int steps; - Point(int r, int c) { + Point(int r, int c, int steps) { this.r = r; this.c = c; + this.steps = steps; } public String toString() { @@ -49,10 +53,14 @@ public String toString() { } public static void main(String[] args) { - char[][] grid = {{'O', 'O', 'O', 'O'}, + + char[][] grid = + {{'O', 'O', 'O', 'O'}, {'D', 'O', 'D', 'O'}, {'O', 'O', 'O', 'O'}, {'X', 'D', 'D', 'O'}}; + + System.out.println(minSteps(grid)); } } \ No newline at end of file diff --git a/src/geeksforgeeks/TreasureIslandII.java b/src/main/java/practiceproblems/TreasureIslandII.java similarity index 84% rename from src/geeksforgeeks/TreasureIslandII.java rename to src/main/java/practiceproblems/TreasureIslandII.java index 3d4237f..80538ad 100644 --- a/src/geeksforgeeks/TreasureIslandII.java +++ b/src/main/java/practiceproblems/TreasureIslandII.java @@ -1,10 +1,12 @@ -package geeksforgeeks; +package practiceproblems; import java.util.ArrayDeque; import java.util.Queue; -/*https://leetcode.com/discuss/interview-question/356150/amazon-oa-2019-shortest-path-from-multiple-sources*/ -// unresolved +/** + * https://leetcode.com/discuss/interview-question/356150/amazon-oa-2019-shortest-path-from-multiple-sources + * */ + public class TreasureIslandII { private static final int[][] DIRS = {{0, 1}, {1, 0}, {-1, 0}, {0, -1}}; @@ -61,8 +63,12 @@ public String toString() { } public static void main(String[] args) { - char[][] grid = {{'S', 'O', 'O', 'S', 'S'}, {'D', 'O', 'D', 'O', 'D'}, {'O', 'O', 'O', 'O', 'X'}, - {'X', 'D', 'D', 'O', 'O'}, {'X', 'D', 'D', 'D', 'O'}}; + char[][] grid = { + {'S', 'O', 'O', 'S', 'S'}, + {'D', 'O', 'D', 'O', 'D'}, + {'O', 'O', 'O', 'O', 'X'}, + {'X', 'D', 'D', 'O', 'O'}, + {'X', 'D', 'D', 'D', 'O'}}; test(minDist(grid), 3); } diff --git a/src/main/java/practiceproblems/Trie.java b/src/main/java/practiceproblems/Trie.java new file mode 100644 index 0000000..6cc36d9 --- /dev/null +++ b/src/main/java/practiceproblems/Trie.java @@ -0,0 +1,64 @@ +package practiceproblems; + +class Trie { + + static class TrieNode{ + char data; + TrieNode[] children; + boolean isWord; + TrieNode(char data){ + this.data=data; + this.children= new TrieNode[26]; + } + } + + + TrieNode root; + public Trie() { + this.root=new TrieNode(' '); + } + + + public void insert(String word) { + TrieNode head=root; + + for(int i=0;i 10+30+400+30= 470 + // we need to remove one half to save some money + // what we can do is take the difference in each input + // (10-20) (30-200) (400-50) (30-20) + // [-10, -170, 350, 10] + // what the above array indicates is that for i'th candidate=>[10,20] we will save 10 in sending to A + // when i=2 we will have to spend 350 more to bring to A city so we sort by arr[0]-arr[1] + // greedily we take negative val candidates to A and rest to B for this input + + PriorityQueue queue= new PriorityQueue<>((a, b)->(a[0] - a[1]) - (b[0] - b[1])); + + for(int[] cost: costs){ + queue.offer(cost); + } + int result=0; + int k=0; + while(k + * 🎯 lets talk about character 'c' that in how many substrings this 'c' will be counted as unique string + * ::: left no of characters on L.H.S of 'c' = left + * ::: left no of characters on R.H.S of 'c' = right + * Then total possible substrings containing this 'c' = (left+1) x (right+1) + *

+ * Thus contributions for c = 2 * 3 = 6 + * and those 6 substrings are ( bc, bcd, bcde, c, cd, cde ) + *

+ * So we can say that every character will have contribution of (left+1)x(right+1) to the ans. + *

+ * 🎯 But there is one small catch here ! + * What if characters are same ?? then only one of those characters will have contribution in any substring containing them. + * "acca" + * Here first 'c' will for sure have a contribution = (left+1)x(right+1) = 2 x 3 = 6 + * but for second 'c' Left region will be reduced till the rightmost 'c' on leftside. So the value of left for second 'c' will be = 0 !! + * Right will have no change. + * So contribution for second 'c' = 1 x 2 = 2; + *

+ * This way we can calculate the contribution for each and every character in just one for loop. + */ + public long appealSum(String s) { + int l = s.length(); + int[] lo = new int[26]; //store the last occurrence index of every character + Arrays.fill(lo, -1); + long ans = 0; + for (int i = 0; i < l; i++) { + int cc = s.charAt(i) - 'a'; + int right = l - i - 1; + int left; + + int last = lo[cc]; + if (last == -1) left = i; + else + left = (i - last) - 1; // this is to ensure that contribution for duplicate char will skip from the prev last seen position + ans += (left + 1) * (right + 1); + lo[cc] = i; + } + return ans; + + } +} diff --git a/src/main/java/practiceproblems/UniqueElementsInArray.java b/src/main/java/practiceproblems/UniqueElementsInArray.java new file mode 100644 index 0000000..24e7622 --- /dev/null +++ b/src/main/java/practiceproblems/UniqueElementsInArray.java @@ -0,0 +1,29 @@ +package practiceproblems; + +/** + * https://codepumpkin.com/find-unique-array-element/#XORApproach + */ +public class UniqueElementsInArray { + + /** + * @param inputArray + * + * @return returns unique Element in the array. + * -1 if no unique element is available in the array. + */ + // 0 0 : 0 || 1 1 : 0 + // returns 0 if both are same + public static int xorApproach(int[] inputArray) { + int result = 0; + for (int i = 0; i < inputArray.length; i++) { + result ^= inputArray[i]; + } + + return (result > 0 ? result : -1); + } + + public static void main(String[] args) { + int[] nums = { 1, 2, 3, 2, 1, 4, 3 }; + xorApproach(nums); + } +} diff --git a/src/main/java/practiceproblems/UniquePath.java b/src/main/java/practiceproblems/UniquePath.java new file mode 100644 index 0000000..3179712 --- /dev/null +++ b/src/main/java/practiceproblems/UniquePath.java @@ -0,0 +1,107 @@ +package practiceproblems; + +/*https://leetcode.com/problems/unique-paths-ii/ + https://leetcode.com/problems/unique-paths/*/ +public class UniquePath { + + public static void main(String[] args) { + System.out.println(uniquePathI(3, 2)); + + int[][] matrix = {{0, 0, 0}, {0, 1, 0}, {0, 0, 0}}; + + System.out.println(uniquePathII(matrix)); + + } + + private static int uniquePathI(int row, int col) { + int[][] dp = new int[row][col]; + + for (int i = 0; i < col; i++) { + dp[0][i] = 1; + } + + for (int j = 0; j < row; j++) { + dp[j][0] = 1; + } + + for (int i = 1; i < row; i++) { + for (int j = 1; j < col; j++) { + dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; + } + } + return dp[row - 1][col - 1]; + } + + /** + * Now consider if some obstacles are added to the grids. How many unique paths would there be? + * Input: + * [ + * [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 + */ + private static int uniquePathII(int[][] obstacleGrid) { + + if (obstacleGrid[0][0] == 1) + return 0; + int m = obstacleGrid.length; + int n = obstacleGrid[0].length; + + int[][] dp = new int[obstacleGrid.length][obstacleGrid[0].length]; + + for (int i = 0; i < m; i++) { + if (obstacleGrid[i][0] == 1) { + dp[i][0] = 0; + break; + } else { + dp[i][0] = 1; + } + } + + for (int j = 0; j < n; j++) { + if (obstacleGrid[0][j] == 1) { + dp[0][j] = 0; + break; + } else { + dp[0][j] = 1; + } + } + + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + if (obstacleGrid[i][j] == 1) { + dp[i][j] = 0; + } else { + dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; + } + } + } + return dp[m - 1][n - 1]; + + } + + Integer[][] cache; + + public int uniquePathsWithObstacles(int[][] obstacleGrid) { + cache = new Integer[obstacleGrid.length][obstacleGrid[0].length]; + return recursionHelper(obstacleGrid, 0, 0); + } + + public int recursionHelper(int[][] arr, int i, int j) { + if (i < 0 || j < 0 || i >= arr.length || j >= arr[0].length || arr[i][j] == 1) return 0; + + if (i == arr.length - 1 && j == arr[0].length - 1) return 1; + + if (cache[i][j] != null) return cache[i][j]; + + return cache[i][j] = recursionHelper(arr, i, j + 1) + recursionHelper(arr, i + 1, j); + + } +} diff --git a/src/geeksforgeeks/UniquePathMaximum.java b/src/main/java/practiceproblems/UniquePathMaximum.java similarity index 53% rename from src/geeksforgeeks/UniquePathMaximum.java rename to src/main/java/practiceproblems/UniquePathMaximum.java index ab7b7aa..cdbd1c9 100644 --- a/src/geeksforgeeks/UniquePathMaximum.java +++ b/src/main/java/practiceproblems/UniquePathMaximum.java @@ -1,13 +1,32 @@ -package geeksforgeeks; +package practiceproblems; /* https://leetcode.com/discuss/interview-question/383669/ + +// revision */ +import java.util.Arrays; + +/** + * find the maximum score of a path starting at [0, 0] and ending at [r-1, c-1]. The score of a path is the minimum value in that path. + *

+ * Input: + * [[1, 2, 3] + * [4, 5, 1]] + *

+ * Output: 4 + * Explanation: + * Possible paths: + * 1-> 2 -> 3 -> 1 + * 1-> 2 -> 5 -> 1 + * 1-> 4 -> 5 -> 1 + * So min of all the paths = [2, 2, 4]. Note that we don't include the first and final entry. + */ public class UniquePathMaximum { public static void main(String[] args) { - int[][] matrix = {{6, 7, 8}, {5, 4, 2}, {8, 7, 6}}; + int[][] matrix = { { 6, 7, 8 }, { 5, 4, 2 }, { 8, 7, 6 } }; System.out.println(findMaximumOfUniquePath(matrix)); } @@ -22,11 +41,14 @@ private static int findMaximumOfUniquePath(int[][] matrix) { for (int i = 1; i < matrix.length; i++) { for (int j = 1; j < matrix[i].length; j++) { - matrix[i][j] = Math.max(Math.min(matrix[i - 1][j], matrix[i][j]), Math.min(matrix[i][j - 1], matrix[i][j])); + matrix[i][j] = Math.max(Math.min(matrix[i - 1][j], matrix[i][j]), + Math.min(matrix[i][j - 1], matrix[i][j])); } } + + for (int[] ints : matrix) { + System.out.println(Arrays.toString(ints)); + } return matrix[matrix.length - 1][matrix[0].length - 1]; } - - } \ No newline at end of file diff --git a/src/main/java/practiceproblems/UpdateMatrix.java b/src/main/java/practiceproblems/UpdateMatrix.java new file mode 100644 index 0000000..5172905 --- /dev/null +++ b/src/main/java/practiceproblems/UpdateMatrix.java @@ -0,0 +1,116 @@ +package practiceproblems; + +import java.util.ArrayDeque; +import java.util.Queue; + +/** + * https://leetcode.com/problems/01-matrix/ + * + * 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. + */ +public class UpdateMatrix { + + /** + * Naive BFS invoked multiple times (Slow) + * i) Iterate over the matrix with a nested for-loop to find cells containing 1 + * ii) Apply BFS algo on those cells -> pass those cells to a BFS helper to find distance to closest 0 + * iii) update the matrix cell with the distance + *

+ * Optimized BFS invoked only once (Fast) + * Idea + *

+ * Instead of invoking BFS for each land cell to see how far we can get away from that source, we flip the problem. + * The flipped problem is to start from target (sea) and to figure our the closest source (land) + * This allows us to run a single BFS search that emerges from different places + * (all the targets aka all the zero cells) in the grid + *

+ * Add all the targets (all zero cells) into the queue. + * While you're at it, also mark those targets as visited (add to a visited set) + *

+ * Run a single BFS on the pre-processed queue and investigate neighbours. + *

+ * if neighbour cell has not been visited --> then it must bea land cell + * (since all the sea cells have been marked visited): + * append the neighbour cell into the queue and mutate the gird + * + * @param matrix + * @return + */ + public int[][] updateMatrix(int[][] matrix) { + Queue queue = new ArrayDeque<>(); + int m = matrix.length, n = matrix[0].length; + boolean[][] visited = new boolean[m][n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (matrix[i][j] == 0) { + queue.offer(new int[]{i, j}); + visited[i][j] = true; + } + } + } + int[][] dir = new int[][]{{1, 0}, {-1, 0}, {0, 1}, {0, -1}}; + while (!queue.isEmpty()) { + int[] cur = queue.poll(); + for (int i = 0; i < 4; i++) { + int row = cur[0] + dir[i][0]; + int col = cur[1] + dir[i][1]; + if (row < 0 || row >= m || col < 0 || col >= n || visited[row][col]) { + continue; + } + visited[row][col] = true; + /** + * since, we popped out the cell (cell(0),cell(1)) and now looking at all its four adjacent cells + * and since we're sure that the cell has its minimum distance from zero , + * so, in case any of its four cells value(calling it child cell) + * (which is child's distance from zero cell except when its Max Integer value) + * has value more than the {value in cell} + 1 , + * we update the child cell to {value in cell} + 1 .. + 1 is used because + * when add +1 as going from a cell to next adjacent cell increases path by 1 + */ + matrix[row][col] = matrix[cur[0]][cur[1]] + 1; + queue.offer(new int[]{row, col}); + } + } + return matrix; + } + + /** + * In this problem, a cell has at most 4 neighbors that are left, top, right, bottom. + * If we use dynamic programming to compute the distance of the current cell based on 4 neighbors simultaneously, + * it's impossible because we are not sure if distance of neighboring cells is already computed or not + * + * For those who are asking why DP is done in two passes, in DP we can only use the values which are previously calculated. + * When we are parsing from top left and coming down to bottom right, + * we can only use the values of above and left because only those two values are precomputed, + * if we take right and down, those values are not yet computed, + * if we work with those values we will get suboptimal answer. + */ + public int[][] updateMatrixDP(int[][] matrix) { + int maxValue = matrix.length * matrix[0].length; + + for (int i = 0; i < matrix.length; i++) { + for (int j = 0; j < matrix[0].length; j++) { + if (matrix[i][j] == 0) continue; + + int top = i - 1 >= 0 ? matrix[i - 1][j] : maxValue; + int left = j - 1 >= 0 ? matrix[i][j - 1] : maxValue; + + matrix[i][j] = Math.min(top, left) + 1; + } + } + + for (int i = matrix.length - 1; i >= 0; i--) { + for (int j = matrix[0].length - 1; j >= 0; j--) { + int bottom = i + 1 < matrix.length ? matrix[i + 1][j] : maxValue; + int right = j + 1 < matrix[0].length ? matrix[i][j + 1] : maxValue; + + matrix[i][j] = Math.min(matrix[i][j], Math.min(bottom, right) + 1); + } + } + + return matrix; + + } +} diff --git a/src/main/java/practiceproblems/UrlEncode.java b/src/main/java/practiceproblems/UrlEncode.java new file mode 100644 index 0000000..b5cc11d --- /dev/null +++ b/src/main/java/practiceproblems/UrlEncode.java @@ -0,0 +1,45 @@ +package practiceproblems; + +import java.util.stream.IntStream; + +/** + * Write a method to replace all the spaces in a string with ‘%20’. + * You may assume that the string has sufficient space at the end to hold the additional characters, + * and that you are given the “true” length of the string. + */ +public class UrlEncode { + public void replaceSpaces(char[] str, int trueLength) { + int i = str.length - 1; + int extra = str.length - trueLength; + int j = i - extra; + while (i != j) { + if (!Character.isSpaceChar(str[j])) { + str[i--] = str[j--]; + } else { + str[i--] = '0'; + str[i--] = '2'; + str[i--] = '%'; + j--; + } + } + } + + public static void main(String[] args) { + test(new char[17], "Mr John Smith"); + test(new char[12], "Mr John "); + test(new char[5], "Mr "); + test(new char[5], " Mr"); + test(new char[8], " Mr "); + test(new char[3], " "); + test(new char[20], "Mr John Smith"); + test(new char[0], ""); + } + + private static void test(char[] str, String s) { + IntStream.range(0, s.length()).forEach(i -> str[i] = s.charAt(i)); + System.out.print(new String(str) + " - "); + UrlEncode ob = new UrlEncode(); + ob.replaceSpaces(str, s.length()); + System.out.println(new String(str)); + } +} \ No newline at end of file diff --git a/src/geeksforgeeks/ValidPalindromeII.java b/src/main/java/practiceproblems/ValidPalindromeII.java similarity index 66% rename from src/geeksforgeeks/ValidPalindromeII.java rename to src/main/java/practiceproblems/ValidPalindromeII.java index 6036c1a..a8eb6aa 100644 --- a/src/geeksforgeeks/ValidPalindromeII.java +++ b/src/main/java/practiceproblems/ValidPalindromeII.java @@ -1,5 +1,8 @@ -package geeksforgeeks; +package practiceproblems; +/** + * https://leetcode.com/problems/valid-palindrome-ii/ + */ class ValidPalindromeII { public static boolean validPalindrome(String s) { @@ -9,10 +12,11 @@ public static boolean validPalindrome(String s) { j--; } - if (i >= j) return true; + if (i >= j) { + return true; + } - if (isPalindrome(s, i + 1, j) || isPalindrome(s, i, j - 1)) return true; - return false; + return isPalindrome(s, i + 1, j) || isPalindrome(s, i, j - 1); } private static boolean isPalindrome(String s, int i, int j) { @@ -20,7 +24,9 @@ private static boolean isPalindrome(String s, int i, int j) { if (s.charAt(i) == s.charAt(j)) { i++; j--; - } else return false; + } else { + return false; + } } return true; } diff --git a/src/main/java/practiceproblems/ValidSudoku.java b/src/main/java/practiceproblems/ValidSudoku.java new file mode 100644 index 0000000..955dff0 --- /dev/null +++ b/src/main/java/practiceproblems/ValidSudoku.java @@ -0,0 +1,35 @@ +package practiceproblems; + +import java.util.HashSet; +import java.util.Set; + +public class ValidSudoku +{ + public boolean isValidSudoku(char[][] board) { + Set seen = new HashSet<>(); + + for (int i = 0; i < 9; i++) { + for (int j = 0; j < 9; j++) { + if (board[i][j] != '.') { + char number = board[i][j]; + if (!seen.add(number + "seen in row" + i) || !seen.add(number + "seen in col" + j) + || !seen.add(number + "seen in block" + i / 3 + "-" + j / 3)) { + return false; + } + } + } + } + + return true; + } + public static void main(String[] args) { + char[][] 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' } }; + + ValidSudoku vs = new ValidSudoku(); + System.out.println(vs.isValidSudoku(board)); + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/ValidateIpAddresses.java b/src/main/java/practiceproblems/ValidateIpAddresses.java new file mode 100644 index 0000000..17aa32b --- /dev/null +++ b/src/main/java/practiceproblems/ValidateIpAddresses.java @@ -0,0 +1,65 @@ +package practiceproblems; + +/** + * 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 but "192.168.01.1", + * while "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, + * lower-case 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: IP = "172.16.254.1" + * Output: "IPv4" + * Explanation: This is a valid IPv4 address, return "IPv4". + * + * Input: IP = "2001:0db8:85a3:0:0:8A2E:0370:7334" + * Output: "IPv6" + * Explanation: This is a valid IPv6 address, return "IPv6". + */ +public class ValidateIpAddresses { + // the condition for IPv4 is + // there should be 4 components separated by 3 dots + // each component should have value between 0-9 (base 10) + public String validIPAddress(String IP) { + if(IP==null || IP.length()==0) return "Neither"; + + if(IP.chars().filter(e->e=='.').count()==3){ + + for(String s: IP.split("\\.",-1)){ //-1 is for edge case like "1.0.1." + if(s.length()==0 || s.length()>4) return "Neither"; + if(s.charAt(0)=='0' && s.length()!=1) return "Neither"; + for(char c:s.toCharArray()) if(!Character.isDigit(c)) return "Neither"; + if(Integer.parseInt(s)>255) return "Neither"; + } + + return "IPv4"; + + } + // the condition for IPv6 is + // it should have 8 components, separated by 7 ':'s + // each component should have hex-value i.e 0-9, a-f or A-F + else if(IP.chars().filter(e->e==':').count()==7){ + for(String s: IP.split(":",-1)){ + if(s.length()==0 || s.length()>4) return "Neither"; + for(char c: s.toCharArray()){ + if(!((c>='0' && c<='9') || (c>='a' && c<='f') || (c>='A' && c<='F'))){ + return "Neither"; + } + } + } + + return "IPv6"; + } + + return "Neither"; + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/VulgarDecimal.java b/src/main/java/practiceproblems/VulgarDecimal.java new file mode 100644 index 0000000..f7e972b --- /dev/null +++ b/src/main/java/practiceproblems/VulgarDecimal.java @@ -0,0 +1,56 @@ +package practiceproblems; + +import java.util.HashMap; +import java.util.Map; + +/** + * https://leetcode.com/problems/fraction-to-recurring-decimal + * tricky + */ +public class VulgarDecimal { + + public String fractionToDecimal(int numerator, int denominator) { + + boolean isNegative = numerator < 0 && denominator > 0 || numerator > 0 && denominator < 0; + + StringBuilder sb = new StringBuilder(); + Map map = new HashMap<>(); + long numeratorL = Math.abs(numerator); + long denominatorL = Math.abs(denominator); + + long rem = numeratorL / denominatorL; + sb.append(rem); + + if (isNegative) sb.insert(0, '-'); + + if (numeratorL % denominatorL > 0) { + sb.append("."); + } else { + return sb.toString(); + } + numeratorL %= denominatorL; + map.put(numeratorL, sb.length()); + + while (numeratorL > 0) { + numeratorL *= 10; + + rem = numeratorL / denominatorL; + sb.append(rem); + numeratorL %= denominatorL; + + if (map.containsKey(numeratorL)) { + int pos = map.get(numeratorL); + sb.insert(pos, "("); + sb.append(")"); + break; + } else { + map.put(numeratorL, sb.length()); + } + + } + + return sb.toString(); + } + + +} diff --git a/src/main/java/practiceproblems/WordBreak.java b/src/main/java/practiceproblems/WordBreak.java new file mode 100644 index 0000000..f748b45 --- /dev/null +++ b/src/main/java/practiceproblems/WordBreak.java @@ -0,0 +1,117 @@ +package practiceproblems; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +class WordBreak { + +/** + * +|T| | | | | | | | | + 0 1 2 3 4 5 6 7 8 + +i = 1 +j = 0 sub = l + +i = 2 +j = 0 sub = le +j = 1 sub = e + +i = 3 +j = 0 sub = lee +j = 1 sub = ee +j = 2 sub = e + +i = 4 +j = 0 sub = leet && T[0] and then break, no need to check for rest +|T | | | |T| | | | | +0 1 2 3 4 5 6 7 8 + +i = 5 +j = 0 sub = leetc +j = 1 sub = eetc +j = 2 sub = etc +j = 3 sub = tc +j = 4 sub = c + +i = 6 +j = 0 sub = leetco +j = 1 sub = eetco +j = 2 sub = etco +j = 3 sub = tco +j = 4 sub = co +j = 5 sub = o + +i = 7 +j = 0 sub = leetcod +j = 1 sub = eetcod +j = 2 sub = etcod +j = 3 sub = tcod +j = 4 sub = cod +j = 5 sub = od +j = 6 sub = d + +i = 8 +j = 0 sub = leetcode +j = 1 sub = eetcode +j = 2 sub = etcode +j = 3 sub = tcode +j = 4 sub = code && T[4] and then break + +|T| | | |T| | | |T| + 0 1 2 3 4 5 6 7 8 +*/ + public boolean wordBreak(String s, List wordDict) { + if (s == null) { + return false; + } + boolean[] dp = new boolean[s.length() + 1]; + dp[0] = true; + Set set = new HashSet<>(wordDict); + + for (int i = 1; i <= s.length(); i++) { + for (int j = 0; j < i; j++) { + dp[i] = dp[j] && set.contains(s.substring(j, i)); + if (dp[i]) { + break; + } + } + } + + return dp[s.length()]; + } + + public boolean wordBreakRec(String s, List wordDict) { + // create the memoization array to save results and avoid repeat computations + Boolean[] canBreak = new Boolean[s.length()]; + + // convert the list into set for faster lookup + Set wordSet = new HashSet<>(wordDict); + + // recursion with memoization + return helper(s, 0, wordSet, canBreak); + } + + private boolean helper(String s, int startIdx, Set wordSet, Boolean[] canBreak) { + // in case we've reached the end of string, return true + if (startIdx == s.length()) return true; + // else if we've already computed on current substring before + if (canBreak[startIdx] != null) return canBreak[startIdx]; // auto-unboxing + + boolean res = false; + // iterate through all indices after startIdx, explore every possible word + for (int i = startIdx + 1; i <= s.length(); i++) { + String currWord = s.substring(startIdx, i); + // skip if this is not a word in the input dictionary + // recursively call upon the rest of string + if (wordSet.contains(currWord) && helper(s, i, wordSet, canBreak)) { + res = true; + break; + } + } + // add result to memo and return the result + canBreak[startIdx] = res; + return res; + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/WordBreakII.java b/src/main/java/practiceproblems/WordBreakII.java new file mode 100644 index 0000000..a4e0132 --- /dev/null +++ b/src/main/java/practiceproblems/WordBreakII.java @@ -0,0 +1,37 @@ +package practiceproblems; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * tricky WordBreak + */ +public class WordBreakII { + public List wordBreak(String s, List wordDict) { + Map> cache = new HashMap<>(); + backtrack(s, wordDict, cache); + return cache.get(s); + } + + public List backtrack(String s, List wordDict, Map> cache) { + + if (cache.containsKey(s)) return cache.get(s); + + List result = new ArrayList<>(); + for (String word : wordDict) { + if (!s.startsWith(word)) continue; // string does not start with this word? + String next = s.substring(word.length()); + if (next.isEmpty()) { + result.add(word); + } else { + for (String sub : backtrack(next, wordDict, cache)) { + result.add(word + " " + sub); + } + } + } + cache.put(s, result); + return result; + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/WordLadder.java b/src/main/java/practiceproblems/WordLadder.java new file mode 100644 index 0000000..9506a26 --- /dev/null +++ b/src/main/java/practiceproblems/WordLadder.java @@ -0,0 +1,49 @@ +package practiceproblems; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; +import java.util.Set; + +public class WordLadder { + public int ladderLength(String beginWord, String endWord, List wordList) { + Set set = new HashSet<>(wordList); + if (!set.contains(endWord)) return 0; // end word itself not in set + Queue queue = new LinkedList<>(); + queue.add(beginWord); + int level = 1; + + while (!queue.isEmpty()) { + + int size = queue.size(); + for (int i = 0; i < size; i++) { // going level by level + String currentWord = queue.poll(); + char[] charArray = currentWord.toCharArray(); + + for (int j = 0; j < charArray.length; j++) { + char temp = charArray[j]; // storing the value to reset back + for (char ch = 'a'; ch <= 'z'; ch++) { // try all letters in alphabets + if (temp == ch) { + continue; + } + charArray[j] = ch; + String newWord = String.valueOf(charArray); + if (set.contains(newWord)) { + if (newWord.equals(endWord)) { // if found return level + return level + 1; + } + queue.add(newWord);// else add to queue and continue + set.remove(newWord);// because you already reached this word, no need to see again + } + } + charArray[j] = temp; + } + + } + level++; + } + + return 0; + } +} \ No newline at end of file diff --git a/src/geeksforgeeks/WordSearch.java b/src/main/java/practiceproblems/WordSearch.java similarity index 75% rename from src/geeksforgeeks/WordSearch.java rename to src/main/java/practiceproblems/WordSearch.java index 9c184db..206f480 100644 --- a/src/geeksforgeeks/WordSearch.java +++ b/src/main/java/practiceproblems/WordSearch.java @@ -1,7 +1,14 @@ -package geeksforgeeks; +package practiceproblems; /** * https://leetcode.com/problems/word-search/ + * + * tricky dfs + * + * I don't think BFS is feasible here. + * This is because we would need to separately track visited_elements for each entry in the queue which will increase the Space Complexity to skyrocket. + * Every time you dequeue and move in 3 other directions (worst case), you will be creating clones of the visited array for each of 3 directions. + * In case of BFS, space complexity will be O(nm3^(n^2 * m^2)) */ public class WordSearch { diff --git a/src/main/java/practiceproblems/WordSearchII.java b/src/main/java/practiceproblems/WordSearchII.java new file mode 100644 index 0000000..8ec61fd --- /dev/null +++ b/src/main/java/practiceproblems/WordSearchII.java @@ -0,0 +1,57 @@ +package practiceproblems; + +import java.util.ArrayList; +import java.util.List; + +/** + * TODO- revision + * https://leetcode.com/problems/word-search-ii/ + */ +public class WordSearchII { + public List findWords(char[][] board, String[] words) { + List res = new ArrayList<>(); + TrieNode root = buildTrie(words); + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[0].length; j++) { + dfs(board, i, j, root, res); + } + } + return res; + } + + public void dfs(char[][] board, int i, int j, TrieNode p, List res) { + char c = board[i][j]; + if (c == '#' || p.children[c - 'a'] == null) return; + p = p.children[c - 'a']; + if (p.word != null) { // found one + res.add(p.word); + p.word = null; // de-duplicate + } + + board[i][j] = '#'; + if (i > 0) dfs(board, i - 1, j, p, res); + if (j > 0) dfs(board, i, j - 1, p, res); + if (i < board.length - 1) dfs(board, i + 1, j, p, res); + if (j < board[0].length - 1) dfs(board, i, j + 1, p, res); + board[i][j] = c; + } + + public TrieNode buildTrie(String[] words) { + TrieNode root = new TrieNode(); + for (String w : words) { + TrieNode p = root; + for (char c : w.toCharArray()) { + int i = c - 'a'; + if (p.children[i] == null) p.children[i] = new TrieNode(); + p = p.children[i]; + } + p.word = w; + } + return root; + } + + static class TrieNode { + TrieNode[] children = new TrieNode[26]; + String word; + } +} diff --git a/src/main/java/practiceproblems/design/AllOneDataStructure.java b/src/main/java/practiceproblems/design/AllOneDataStructure.java new file mode 100644 index 0000000..130a20d --- /dev/null +++ b/src/main/java/practiceproblems/design/AllOneDataStructure.java @@ -0,0 +1,112 @@ +package practiceproblems.design; + +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +/** + * TODO + */ +public class AllOneDataStructure { + + class ValueNode { + ValueNode prev, next; + int val; + Set strs; + + ValueNode(int v) { + val = v; + strs = new LinkedHashSet<>(); + } + + void insertAt(ValueNode node) { + next = node; + prev = node.prev; + next.prev = this; + prev.next = this; + } + + void remove(String str) { + strs.remove(str); + if (strs.isEmpty()) { + prev.next = next; + next.prev = prev; + } + } + } + + ValueNode valueHead, valueTail; // dummy + Map keys; + + /** + * Initialize your data structure here. + */ + public AllOneDataStructure() { + valueHead = new ValueNode(0); + valueTail = new ValueNode(0); + valueHead.next = valueTail; + valueTail.prev = valueHead; + keys = new HashMap<>(); + } + + /** + * Inserts a new key with value 1. Or increments an existing key by 1. + */ + public void inc(String key) { + ValueNode node = keys.getOrDefault(key, valueHead); + ValueNode vn = node.next; + if (vn.val != node.val + 1) { + vn = new ValueNode(node.val + 1); + vn.insertAt(node.next); + } + vn.strs.add(key); + keys.put(key, vn); + if (node != valueHead) node.remove(key); + } + + /** + * Decrements an existing key by 1. If Key's value is 1, remove it from the data structure. + */ + public void dec(String key) { + ValueNode node = keys.get(key); + if (node == null) return; + if (node.val == 1) { + keys.remove(key); + node.remove(key); + return; + } + ValueNode vn = node.prev; + if (vn.val != node.val - 1) { + vn = new ValueNode(node.val - 1); + vn.insertAt(node); + } + vn.strs.add(key); + keys.put(key, vn); + node.remove(key); + } + + /** + * Returns one of the keys with maximal value. + */ + public String getMaxKey() { + if (valueTail.prev == valueHead) return ""; + return valueTail.prev.strs.iterator().next(); + } + + /** + * Returns one of the keys with Minimal value. + */ + public String getMinKey() { + if (valueHead.next == valueTail) return ""; + return valueHead.next.strs.iterator().next(); + } + + public static void main(String[] args) { + AllOneDataStructure o = new AllOneDataStructure(); + o.inc("Hello"); + o.inc("Hello"); + o.inc("Leet"); + o.dec("Hello"); + } +} diff --git a/src/main/java/practiceproblems/design/AuthenticationManager.java b/src/main/java/practiceproblems/design/AuthenticationManager.java new file mode 100644 index 0000000..8f7472a --- /dev/null +++ b/src/main/java/practiceproblems/design/AuthenticationManager.java @@ -0,0 +1,54 @@ +package practiceproblems.design; + +import java.util.TreeMap; + +/** + * https://leetcode.com/problems/design-authentication-manager/ + */ +public class AuthenticationManager { + + int timeToLive; + TreeMap authCache; + TreeMap timeCache; + + /** + * AuthenticationManager(int timeToLive) constructs the AuthenticationManager and sets the timeToLive. + */ + public AuthenticationManager(int timeToLive) { + this.timeToLive = timeToLive; + authCache = new TreeMap<>(); + timeCache = new TreeMap<>(); + + } + + /** + * generate(string tokenId, int currentTime) generates a new token with the given tokenId at the given currentTime in seconds. + */ + public void generate(String tokenId, int currentTime) { + authCache.put(tokenId, currentTime); + timeCache.put(currentTime, tokenId); + } + + /** + * renews the unexpired token with the given tokenId at the given currentTime in seconds. + * If there are no unexpired tokens with the given tokenId, the request is ignored, and nothing happens. + */ + public void renew(String tokenId, int currentTime) { + if (!authCache.containsKey(tokenId)) return; + + int timeOfToken = authCache.get(tokenId); + + if (timeOfToken + timeToLive > currentTime) { + timeCache.remove(timeOfToken); + authCache.put(tokenId, currentTime); + timeCache.put(currentTime, tokenId); + } + } + + /** + * returns the number of unexpired tokens at the given currentTime. + */ + public int countUnexpiredTokens(int currentTime) { + return timeCache.subMap(currentTime - timeToLive + 1, currentTime).size(); + } +} diff --git a/src/main/java/practiceproblems/design/AutoCompleteSystem.java b/src/main/java/practiceproblems/design/AutoCompleteSystem.java new file mode 100644 index 0000000..cb09986 --- /dev/null +++ b/src/main/java/practiceproblems/design/AutoCompleteSystem.java @@ -0,0 +1,98 @@ +package practiceproblems.design; + +import java.util.*; + +/** + * tricky trie + * https://cheonhyangzhang.gitbooks.io/leetcode-solutions/content/642-design-search-autocomplete-system.html + */ +class AutoCompleteSystem { + + static class TrieNode { + Map children; + Map counts; + boolean isWord; + + public TrieNode() { + children = new HashMap<>(); + counts = new HashMap<>(); + isWord = false; + } + } + + TrieNode root; // points to the root of the trie to be initialised + String prefix; // concat and stores the input char sequence from main function + + public AutoCompleteSystem(String[] sentences, int[] times) { + root = new TrieNode(); + prefix = ""; + // for the given word and freq value we proceed to add it to trie + for (int i = 0; i < sentences.length; i++) { + add(sentences[i], times[i]); + } + } + + private void add(String s, int count) { + TrieNode curr = root; + for (char c : s.toCharArray()) { + curr.children.putIfAbsent(c, new TrieNode()); + curr = curr.children.get(c); + // for every node in the trie(which has one char val) + //the counts map will store the original word along with it's freq value + // for the given input 'i love leetcode' and 'i love you' + // the trie Node(i) => Map(i love leetcode -> 2, i love you->5, island->3, ironman->2) + // / + // Node(' ') => Map(i love leetcode -> 2, i love you->5) + + // the trie Node(i) => Map(i love leetcode -> 2, i love you->5, island->3, ironman->2) + // \ + // Node('s') => Map(island->3) + curr.counts.put(s, curr.counts.getOrDefault(s, 0) + count); + } + curr.isWord = true; + } + + public List input(char c) { + if (c == '#') { + add(prefix, 1); + prefix = ""; + return new ArrayList<>(); + } + prefix = prefix + c; + + TrieNode curr = root; + + for (char ch : prefix.toCharArray()) { + if (!curr.children.containsKey(ch)) { + return new ArrayList<>(); + } + curr = curr.children.get(ch); + } + + Comparator> cmp = (a, b) -> a.getValue().equals(b.getValue()) ? + b.getKey().compareTo(a.getKey()) : + a.getValue() - b.getValue(); + PriorityQueue> pq = new PriorityQueue<>(cmp); + int k = 3; + for (Map.Entry entry : curr.counts.entrySet()) { + pq.offer(entry); + while (!pq.isEmpty() && pq.size() > k) { + pq.poll(); + } + } + + ArrayList res = new ArrayList<>(); + while (!pq.isEmpty()) { + res.add(0, pq.poll().getKey()); + } + return res; + } + + public static void main(String[] args) { + String[] sentences = { "i love you", "island", "ironman", "i love leetcode" }; + int[] times = { 5, 3, 2, 2 }; + AutoCompleteSystem autoCompleteSystem = new AutoCompleteSystem(sentences, times); + System.out.println(autoCompleteSystem.input('i')); + System.out.println(autoCompleteSystem.input(' ')); + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/design/BrowserHistory.java b/src/main/java/practiceproblems/design/BrowserHistory.java new file mode 100644 index 0000000..7041c36 --- /dev/null +++ b/src/main/java/practiceproblems/design/BrowserHistory.java @@ -0,0 +1,69 @@ +package practiceproblems.design; + +/** + * https://leetcode.com/problems/design-browser-history/ + * + * tricky linkedlist + */ +public class BrowserHistory { + + Node head; + Node currentNode; + + public BrowserHistory(String homepage) { + head = new Node("dummy"); + + Node newNode = new Node(homepage); + head.next = newNode; + newNode.prev = head; + currentNode = newNode; + } + + public void visit(String url) { + Node newNode = new Node(url); + head.next = newNode; + newNode.prev = head; + + currentNode.prev = newNode; + newNode.next = currentNode; + currentNode = newNode; + } + + public String back(int steps) { + Node curr = currentNode; + + while (steps > 0 && curr.next != null) { + curr = curr.next; + steps--; + } + currentNode = curr; + return curr.url; + } + + public String forward(int steps) { + Node curr = currentNode; + while (!curr.prev.url.equals("dummy") && steps > 0) { + curr = curr.prev; + steps--; + } + currentNode = curr; + return currentNode.url; + + } + + static class Node { + String url; + Node next; + Node prev; + + public Node(String url) { + this.url = url; + } + + @Override + public String toString() { + return prev + " " + url + " " + next; + } + } +} + diff --git a/src/main/java/practiceproblems/design/DesignCompressedStringIterator.java b/src/main/java/practiceproblems/design/DesignCompressedStringIterator.java new file mode 100644 index 0000000..1c3a959 --- /dev/null +++ b/src/main/java/practiceproblems/design/DesignCompressedStringIterator.java @@ -0,0 +1,54 @@ +package practiceproblems.design; + +/** + * https://leetcode.com/articles/desing-compressed-string-iterator/ + * + * Example 1: + + * Input + * ["StringIterator", "next", "next", "next", "next", "next", "next", "hasNext", "next", "hasNext"] + * [["L1e2t1C1o1d1e1"], [], [], [], [], [], [], [], [], []] + * Output + * [null, "L", "e", "e", "t", "C", "o", true, "d", true] + * Explanation + * StringIterator stringIterator = new StringIterator("L1e2t1C1o1d1e1"); + * stringIterator.next(); // return "L" + * stringIterator.next(); // return "e" + * stringIterator.next(); // return "e" + * stringIterator.next(); // return "t" + * stringIterator.next(); // return "C" + * stringIterator.next(); // return "o" + * stringIterator.hasNext(); // return True + * stringIterator.next(); // return "d" + * stringIterator.hasNext(); // return True + */ +public class DesignCompressedStringIterator { + + String res; + + int ptr = 0; + int num = 0; + char ch = ' '; + + public DesignCompressedStringIterator(String s) { + res = s; + } + + public char next() { + if (!hasNext()) { + return ' '; + } + if (num == 0) { + ch = res.charAt(ptr++); + while (ptr < res.length() && Character.isDigit(res.charAt(ptr))) { + num = num * 10 + res.charAt(ptr++) - '0'; + } + } + num--; + return ch; + } + + public boolean hasNext() { + return ptr != res.length() || num != 0; + } +} diff --git a/src/main/java/practiceproblems/design/DesignFileSystem.java b/src/main/java/practiceproblems/design/DesignFileSystem.java new file mode 100644 index 0000000..f579c37 --- /dev/null +++ b/src/main/java/practiceproblems/design/DesignFileSystem.java @@ -0,0 +1,46 @@ +package practiceproblems.design; + +import java.util.HashMap; + +/** + * https://leetcode.com/problems/design-file-system/ + */ +public class DesignFileSystem { + + private final HashMap cache = new HashMap<>(); + + /** + * Initialization of class. + * Use a hash map to store the path and value. + */ + public DesignFileSystem() { + cache.put("", -1); // avoid initially when path is "/a" regarded as false + } + + /** + * Creates a new path and associates a value to it if possible and returns True. + * The valid path's parent is the path before the last "/". + * Hence, check parent and then put the path into map if it is valid. + * If the path has already exist, return false. + * + * @param path given path + * @param value given value + * @return true to create a new path with value, false if the path already exists or its parent path doesn't exist + */ + public boolean create(String path, int value) { + if (path.charAt(0) != '/') { + return false; + } + String parent = path.substring(0, path.lastIndexOf("/")); + if (!cache.containsKey(parent)) { + return false; + } + + return cache.putIfAbsent(path, value) == null; // if the path exist, m.putIfAbsent(path, value) will be null + } + + public int get(String path) { + return cache.getOrDefault(path, -1); + } + +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/design/DesignHashMap.java b/src/main/java/practiceproblems/design/DesignHashMap.java new file mode 100644 index 0000000..4da03c5 --- /dev/null +++ b/src/main/java/practiceproblems/design/DesignHashMap.java @@ -0,0 +1,85 @@ +package practiceproblems.design; + +public class DesignHashMap { + ListNode[] nodes; + + /** + * Initialize your data structure here. + */ + public DesignHashMap() { + nodes = new ListNode[10000]; + } + + private int idx(int key) { + return Integer.hashCode(key) % nodes.length; + } + + private ListNode find(ListNode node, int val) { + ListNode temp = node, prev = null; + while (temp != null && temp.key != val) { + prev = temp; + temp = temp.next; + } + return prev; + } + + /** + * value will always be non-negative. + */ + public void put(int key, int value) { + int i = idx(key); + if (nodes[i] == null) { + nodes[i] = new ListNode(-1, -1); + } + ListNode prev = find(nodes[i], key); + if (prev.next == null) { + prev.next = new ListNode(key, value); + } else { + prev.next.val = value; + } + } + + /** + * Returns the value to which the specified key is mapped, or -1 if this map contains no mapping for the key + */ + public int get(int key) { + int i = idx(key); + if (nodes[i] == null) { + return -1; + } + ListNode prev = find(nodes[i], key); + if (prev.next == null) { + return -1; + } + return prev.next.val; + } + + /** + * Removes the mapping of the specified value key if this map contains a mapping for the key + */ + public void remove(int key) { + int i = idx(key); + if (nodes[i] == null) { + return; + } + ListNode prev = find(nodes[i], key); + if (prev.next == null) { + return; + } + prev.next = prev.next.next; + } + + class ListNode { + int key; + int val; + ListNode next; + + public ListNode(int key, int val) { + this.key = key; + this.val = val; + } + } +} + + + diff --git a/src/main/java/practiceproblems/design/DesignInMemoryFileSystem.java b/src/main/java/practiceproblems/design/DesignInMemoryFileSystem.java new file mode 100644 index 0000000..00a95e0 --- /dev/null +++ b/src/main/java/practiceproblems/design/DesignInMemoryFileSystem.java @@ -0,0 +1,72 @@ +package practiceproblems.design; +/** + * tricky trie + */ + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +public class DesignInMemoryFileSystem { + class Trie { + boolean isFile = false; + HashMap folders = new HashMap<>(); + String content = ""; + } + + Trie root; + + public DesignInMemoryFileSystem() { + root = new Trie(); + } + + public List ls(String path) { + Trie trie = root; + List files = new ArrayList<>(); + if (!path.equals("/")) { + String[] arr = path.split("/"); + for (int i = 1; i < arr.length; i++) { + trie = trie.folders.get(arr[i]); + } + if (trie.isFile) { + files.add(arr[arr.length - 1]); + return files; + } + } + List res_files = new ArrayList<>(trie.folders.keySet()); + Collections.sort(res_files); + return res_files; + } + + public void mkdir(String path) { + Trie trie = root; + String[] arr = path.split("/"); + // we start from 1st index because "/a/b/c".split("/") => ["", "a", "b", "c"] + for (int i = 1; i < arr.length; i++) { + trie.folders.putIfAbsent(arr[i], new Trie()); + trie = trie.folders.get(arr[i]); + } + } + + public void addContentToFile(String filePath, String content) { + Trie trie = root; + String[] arr = filePath.split("/"); + for (int i = 1; i < arr.length - 1; i++) { + trie = trie.folders.get(arr[i]); + } + trie.folders.putIfAbsent(arr[arr.length - 1], new Trie()); + trie = trie.folders.get(arr[arr.length - 1]); + trie.isFile = true; + trie.content = trie.content + content; + } + + public String readContentFromFile(String filePath) { + Trie trie = root; + String[] arr = filePath.split("/"); + for (int i = 1; i < arr.length - 1; i++) { + trie = trie.folders.get(arr[i]); + } + return trie.folders.get(arr[arr.length - 1]).content; + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/design/DesignStackIncrement.java b/src/main/java/practiceproblems/design/DesignStackIncrement.java new file mode 100644 index 0000000..21e75bf --- /dev/null +++ b/src/main/java/practiceproblems/design/DesignStackIncrement.java @@ -0,0 +1,35 @@ +package practiceproblems.design; + +/** + * tricky sweep line + * https://leetcode.com/problems/design-a-stack-with-increment-operation + */ +public class DesignStackIncrement { + int[] st; + int top; + int maxSize; + + public DesignStackIncrement(int maxSize) { + this.maxSize = maxSize; + st = new int[maxSize]; + top = 0; + } + + public void pushAlter(int x) { + if (top < maxSize) { + st[top++] = x; + } + } + + public int popAlter() { + if (top <= 0) + return -1; + return st[--top]; + } + + public void incrementAlter(int k, int val) { + int l = Math.min(k, top); + for (int i = 0; i < l; i++) + st[i] += val; + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/design/DesignTicTacToe.java b/src/main/java/practiceproblems/design/DesignTicTacToe.java new file mode 100644 index 0000000..ceb2875 --- /dev/null +++ b/src/main/java/practiceproblems/design/DesignTicTacToe.java @@ -0,0 +1,107 @@ +package practiceproblems.design; +/* +Design a Tic-tac-toe game that is played between two players on a n x n grid. + +You may assume the following rules: + +A move is guaranteed to be valid and is placed on an empty block. +Once a winning condition is reached, no more moves is allowed. +A player who succeeds in placing n of their marks in a horizontal, vertical, or diagonal row wins the game. + +Given n = 3, assume that player 1 is "X" and player 2 is "O" in the board. + +TicTacToe toe = new TicTacToe(3) + +toe.move(0, 0, 1); -> Returns 0 (no one wins) +|X| | | +| | | | // Player 1 makes a move at (0, 0). +| | | | + +toe.move(0, 2, 2); -> Returns 0 (no one wins) +|X| |O| +| | | | // Player 2 makes a move at (0, 2). +| | | | + +toe.move(2, 2, 1); -> Returns 0 (no one wins) +|X| |O| +| | | | // Player 1 makes a move at (2, 2). +| | |X| + +toe.move(1, 1, 2); -> Returns 0 (no one wins) +|X| |O| +| |O| | // Player 2 makes a move at (1, 1). +| | |X| + +toe.move(2, 0, 1); -> Returns 0 (no one wins) +|X| |O| +| |O| | // Player 1 makes a move at (2, 0). +|X| |X| + +toe.move(1, 0, 2); -> Returns 0 (no one wins) +|X| |O| +|O|O| | // Player 2 makes a move at (1, 0). +|X| |X| + +toe.move(2, 1, 1); -> Returns 1 (player 1 wins) +|X| |O| +|O|O| | // Player 1 makes a move at (2, 1). +|X|X|X| + */ +public class DesignTicTacToe { + private int[] rows; + private int[] cols; + private int diagonal; + private int antiDiagonal; + + /** Initialize your data structure here. */ + public DesignTicTacToe(int n) { + rows = new int[n]; + cols = new int[n]; + } + + /** Player {player} makes a move at ({row}, {col}). + @param row The row of the board. + @param col The column of the board. + @param player The player, can be either 1 or 2. + @return The current winning condition, can be either: + 0: No one wins. + 1: Player 1 wins. + 2: Player 2 wins. */ + public int move(int row, int col, int player) { + int toAdd = player == 1 ? 1 : -1; + // if A player makes a move on 0,0 we are going to increment index position of row and col at 0,0 + // if B player makes a move on 0,2 we are going to decrement index position at row and col at 0,2 + // after that the count of row at 0 will be 0(balanced moves) like wise if a row or col has value n only + // a player can be adjudged a winner + rows[row] += toAdd; + cols[col] += toAdd; + if (row == col) { + diagonal += toAdd; + } + + if (col + row == cols.length - 1) { + antiDiagonal += toAdd; + } + + int size = rows.length; + if (Math.abs(rows[row]) == size || + Math.abs(cols[col]) == size || + Math.abs(diagonal) == size || + Math.abs(antiDiagonal) == size) { + return player; + } + + return 0; + } + + public static void main(String[] args) { + DesignTicTacToe toe=new DesignTicTacToe(3); + toe.move(0, 0, 1); + toe.move(0, 2, 2); + toe.move(2, 2, 1); + toe.move(1, 1, 2); + toe.move(2, 0, 1); + toe.move(1, 0, 2); + toe.move(2, 1, 1); + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/design/GetRandomWithDuplicates.java b/src/main/java/practiceproblems/design/GetRandomWithDuplicates.java new file mode 100644 index 0000000..3386abb --- /dev/null +++ b/src/main/java/practiceproblems/design/GetRandomWithDuplicates.java @@ -0,0 +1,70 @@ +package practiceproblems.design; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.Set; + +/** + * https://leetcode.com/problems/insert-delete-getrandom-o1-duplicates-allowed/ + * + * tricky + */ +public class GetRandomWithDuplicates { + + ArrayList randomList; + HashMap> cache; + java.util.Random rand = new java.util.Random(); + + /** + * Initialize your data structure here. + */ + + public GetRandomWithDuplicates() { + randomList = new ArrayList<>(); + cache = new HashMap<>(); + } + + /** + * Inserts a value to the collection. Returns true if the collection did not already contain the specified element. + */ + public boolean insert(int val) { + cache.putIfAbsent(val,new LinkedHashSet<>()); + cache.get(val).add(randomList.size()); + randomList.add(val); + return cache.get(val).size() == 1; + } + + /** + *This is the tricky part. We find the index of the element using the HashMap. + * We use the trick discussed in the intuition to remove the element from the list in O(1)O(1)O(1). + * Since the last element in the list gets moved around, we have to update its value in the HashMap. + * We also have to get rid of the index of the element we removed from the HashMap. + */ + public boolean remove(int val) { + if (!cache.containsKey(val) || cache.get(val).isEmpty()) return false; + int removeIndex = cache.get(val).iterator().next(); + + // remove from linkedHashSet + cache.get(val).remove(removeIndex); + + int lastVal = randomList.get(randomList.size() - 1); + + randomList.set(removeIndex, lastVal); + + cache.get(lastVal).add(removeIndex); + + cache.get(lastVal).remove(randomList.size() - 1); + + randomList.remove(randomList.size() - 1); + + return true; + } + + /** + * Get a random element from the collection. + */ + public int getRandom() { + return randomList.get(rand.nextInt(randomList.size())); + } +} diff --git a/src/main/java/practiceproblems/design/HitCounter.java b/src/main/java/practiceproblems/design/HitCounter.java new file mode 100644 index 0000000..680691f --- /dev/null +++ b/src/main/java/practiceproblems/design/HitCounter.java @@ -0,0 +1,38 @@ +package practiceproblems.design; + +import java.util.ArrayDeque; +import java.util.Deque; + +public class HitCounter { + Deque track; + /** + * Initialize your data structure here. + */ + private final int FIVE_MINUTES = 300; + + public HitCounter() { + track = new ArrayDeque<>(); + } + + /** + * Record a hit. + * + * @param timestamp - The current timestamp (in seconds granularity). + */ + public void hit(int timestamp) { + track.addLast(timestamp); + } + + /** + * Return the number of hits in the past 5 minutes. + * + * @param timestamp - The current timestamp (in seconds granularity). + */ + public int getHits(int timestamp) { + while (!track.isEmpty() && track.peekFirst() + FIVE_MINUTES <= timestamp) { + track.removeFirst(); + } + return track.size(); + } + +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/design/LFUCache.java b/src/main/java/practiceproblems/design/LFUCache.java new file mode 100644 index 0000000..7c30cfd --- /dev/null +++ b/src/main/java/practiceproblems/design/LFUCache.java @@ -0,0 +1,181 @@ +package practiceproblems.design; + +import java.util.HashMap; +import java.util.Map; + +/** + * tricky + * revise + */ +public class LFUCache { + /* + * @param capacity: total capacity of LFU Cache + * @param curSize: current size of LFU cache + * @param minFrequency: frequency of the last linked list (the minimum frequency of entire LFU cache) + * @param cache: a hash map that has key to Node mapping, which used for storing all nodes by their keys + * @param frequencyMap: a hash map that has key to linked list mapping, which used for storing all + * double linked list by their frequencies + * */ + + + final int capacity; + int curSize; + int minFrequency; + Map cache; + Map frequencyMap; + + public LFUCache(int capacity) { + + this.capacity = capacity; + this.curSize = 0; + this.minFrequency = 0; + + this.cache = new HashMap<>(); + this.frequencyMap = new HashMap<>(); + } + + /** + * get node value by key, and then update node frequency as well as relocate that node + **/ + public int get(int key) { + DLLNode curNode = cache.get(key); + if (curNode == null) { + return -1; + } + updateNode(curNode); + return curNode.val; + } + + /** + * add new node into LFU cache, as well as double linked list + * condition 1: if LFU cache has input key, update node value and node position in list + * condition 2: if LFU cache does NOT have input key + * - sub condition 1: if LFU cache does NOT have enough space, remove the Least Recent Used node + * in minimum frequency list, then add new node + * - sub condition 2: if LFU cache has enough space, add new node directly + **/ + public void put(int key, int value) { + // corner case: check cache capacity initialization + if (capacity == 0) { + return; + } + + if (cache.containsKey(key)) { + DLLNode curNode = cache.get(key); + curNode.val = value; + updateNode(curNode); + } else { + curSize++; + if (curSize > capacity) { + // get minimum frequency list + DoubleLinkedList minFreqList = frequencyMap.get(minFrequency); + DLLNode deleteNode = minFreqList.removeTail(); + cache.remove(deleteNode.key); + curSize--; + } + // reset min frequency to 1 because of adding new node + minFrequency = 1; + DLLNode newNode = new DLLNode(key, value); + + // get the list with frequency 1, and then add new node into the list, as well as into LFU cache + frequencyMap.putIfAbsent(minFrequency, new DoubleLinkedList()); + frequencyMap.get(minFrequency).addNode(newNode); + cache.put(key, newNode); + } + } + + public void updateNode(DLLNode curNode) { + int curFreq = curNode.frequency; + DoubleLinkedList curList = frequencyMap.get(curFreq); + curList.removeNode(curNode); + + // if current list the last list which has lowest frequency and current node is the only node in that list + // we need to remove the entire list and then increase min frequency value by 1 + if (curFreq == minFrequency && curList.listSize == 0) { + minFrequency++; + } + + curNode.frequency++; + // add current node to another list has current frequency + 1, + // if we do not have the list with this frequency, initialize it + frequencyMap.putIfAbsent(curNode.frequency, new DoubleLinkedList()); + frequencyMap.get(curNode.frequency).addNode(curNode); + } + + /* + * @param key: node key + * @param val: node value + * @param frequency: frequency count of current node + * (all nodes connected in same double linked list has same frequency) + * @param prev: previous pointer of current node + * @param next: next pointer of current node + * */ + static class DLLNode { + int key; + int val; + int frequency; + DLLNode prev; + DLLNode next; + + public DLLNode(int key, int val) { + this.key = key; + this.val = val; + this.frequency = 1; + } + } + + /* + * @param listSize: current size of double linked list + * @param head: head node of double linked list + * @param tail: tail node of double linked list + * */ + static class DoubleLinkedList { + int listSize; + DLLNode head; + DLLNode tail; + + public DoubleLinkedList() { + this.listSize = 0; + this.head = new DLLNode(0, 0); + this.tail = new DLLNode(0, 0); + head.next = tail; + tail.prev = head; + } + + /** + * add new node into head of list and increase list size by 1 + **/ + public void addNode(DLLNode curNode) { + DLLNode nextNode = head.next; + curNode.next = nextNode; + curNode.prev = head; + head.next = curNode; + nextNode.prev = curNode; + listSize++; + } + + /** + * remove input node and decrease list size by 1 + **/ + public void removeNode(DLLNode curNode) { + DLLNode prevNode = curNode.prev; + DLLNode nextNode = curNode.next; + prevNode.next = nextNode; + nextNode.prev = prevNode; + listSize--; + } + + /** + * remove tail node + **/ + public DLLNode removeTail() { + // DO NOT FORGET to check list size + if (listSize > 0) { + DLLNode tailNode = tail.prev; + removeNode(tailNode); + return tailNode; + } + return null; + } + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/design/LRUCache.java b/src/main/java/practiceproblems/design/LRUCache.java new file mode 100644 index 0000000..ca3207f --- /dev/null +++ b/src/main/java/practiceproblems/design/LRUCache.java @@ -0,0 +1,112 @@ +package practiceproblems.design; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +class LRUCache { + + static class DLLNode { + DLLNode prev; + DLLNode next; + int val; + int key; + + public DLLNode(int key, int val) { + this.val = val; + this.key = key; + } + } + + Map map; + DLLNode head; + DLLNode tail; + int capacity = 0; + + public LRUCache(int capacity) { + map = new HashMap<>(); + head = new DLLNode(-1, -1); + tail = new DLLNode(-1, -1); + head.next = tail; + tail.prev = head; + this.capacity = capacity; + } + + public int get(int key) { + if (!map.containsKey(key)) return -1; + DLLNode node = map.get(key); + update(node); + return node.val; + } + + public void put(int key, int value) { + if (map.containsKey(key)) { + DLLNode node = map.get(key); + node.val = value; + update(node); + } else { + if (map.size() >= capacity) { + removeTail(); + } + DLLNode newNode = new DLLNode(key, value); + map.put(key, newNode); + + updateHead(newNode); + } + } + + public void removeTail() { + DLLNode tailNode = tail.prev; + remove(tailNode); + map.remove(tailNode.key); + } + + public void updateHead(DLLNode node) { + node.prev = head; + node.next = head.next; + head.next.prev = node; + head.next = node; + } + + public void remove(DLLNode node) { + DLLNode next = node.next; + DLLNode prev = node.prev; + prev.next = next; + next.prev = prev; + } + + public void update(DLLNode node) { + remove(node); + updateHead(node); + } +} + + +class LRUCache1 { + LinkedHashMap cache; + int capacity = 0; + + public LRUCache1(int capacity) { + cache = new LinkedHashMap<>(capacity, 0.50F, true) { + protected boolean removeEldestEntry(Map.Entry eldest) { + return size() > capacity; + } + }; + this.capacity = capacity; + } + + public int get(int key) { + return cache.getOrDefault(key, -1); + } + + public void put(int key, int value) { + cache.put(key, value); + } + +} +/** + * Your LRUCache object will be instantiated and called as such: + * LRUCache obj = new LRUCache(capacity); + * int param_1 = obj.get(key); + * obj.put(key,value); + */ \ No newline at end of file diff --git a/src/main/java/practiceproblems/design/LeaderBoard.java b/src/main/java/practiceproblems/design/LeaderBoard.java new file mode 100644 index 0000000..7794399 --- /dev/null +++ b/src/main/java/practiceproblems/design/LeaderBoard.java @@ -0,0 +1,66 @@ +package practiceproblems.design; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.TreeSet; + +/** + * https://leetcode.com/problems/design-a-leaderboard/ + */ +public class LeaderBoard { + Map players = new HashMap<>(); + TreeSet scores = new TreeSet<>((a, b) -> b.score - a.score == 0 ? a.id - b.id : b.score - a.score); + + //Log(N) + + /** + * addScore(playerId, score): Update the leaderboard by adding score to the given player's score. + * If there is no player with such id in the leaderboard, add him to the leaderboard with the given score. + */ + public void addScore(int playerId, int score) { + Player cur; + if (players.containsKey(playerId)) { + cur = players.get(playerId); + scores.remove(cur); + cur.score += score; + scores.add(cur); + } else { + cur = new Player(playerId, score); + players.put(playerId, cur); + scores.add(cur); + } + } + + /** + * Return the score sum of the top K players. + */ + public int top(int K) { + Iterator iterator = scores.iterator(); + int res = 0; + while (K-- > 0 && iterator.hasNext()) { + res += iterator.next().score; + } + return res; + } + + /** + * Reset the score of the player with the given id to 0 (in other words erase it from the leaderboard). + * It is guaranteed that the player was added to the leaderboard before calling this function. + */ + public void reset(int playerId) { + Player cur = players.get(playerId); + scores.remove(cur); + cur.score = 0; + } + + class Player { + int id; + int score; + + public Player(int id, int score) { + this.id = id; + this.score = score; + } + } +} diff --git a/src/main/java/practiceproblems/design/LogSystem.java b/src/main/java/practiceproblems/design/LogSystem.java new file mode 100644 index 0000000..8f0919a --- /dev/null +++ b/src/main/java/practiceproblems/design/LogSystem.java @@ -0,0 +1,62 @@ +package practiceproblems.design; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * https://leetcode.com/problems/design-log-storage-system/ + * tricky + */ +public class LogSystem { + + private final String MIN_TIMESTAMP = "2000:01:01:00:00:00"; + private final String MAX_TIMESTAMP = "2017:12:31:23:59:59"; + + HashMap indices; + TreeMap> logStorage; + + public LogSystem() { + indices = new HashMap<>(); + indices.put("Year", 4); + indices.put("Month", 7); + indices.put("Day", 10); + indices.put("Hour", 13); + indices.put("Minute", 16); + indices.put("Second", 19); + + logStorage = new TreeMap<>(); + } + + /** + * Stores the given log (id, timestamp) in your storage system. + */ + public void put(int id, String timestamp) { + logStorage.putIfAbsent(timestamp, new ArrayList<>()); + logStorage.get(timestamp).add(id); + } + + /** + * Returns the IDs of the logs whose timestamps are within the range from start to end inclusive. + * start and end all have the same format as timestamp, and granularity means how precise the range should be (i.e. to the exact Day, Minute, etc.). + * For example, start = "2017:01:01:23:59:59", end = "2017:01:02:23:59:59", and granularity = "Day" + * means that we need to find the logs within the inclusive range from Jan. 1st 2017 to Jan. 2nd 2017, + * and the Hour, Minute, and Second for each log entry can be ignored. + */ + public List retrieve(String start, String end, String granularity) { + + int index = indices.get(granularity); + + String startTime = start.substring(0, index) + MIN_TIMESTAMP.substring(index); + String endTime = end.substring(0, index) + MAX_TIMESTAMP.substring(index); + + List result = new ArrayList<>(); + + for (Map.Entry> entry : logStorage.subMap(startTime, true, endTime, true).entrySet()) { + result.addAll(entry.getValue()); + } + return result; + } +} diff --git a/src/main/java/practiceproblems/design/MaxFreqStack.java b/src/main/java/practiceproblems/design/MaxFreqStack.java new file mode 100644 index 0000000..3411dbb --- /dev/null +++ b/src/main/java/practiceproblems/design/MaxFreqStack.java @@ -0,0 +1,131 @@ +package practiceproblems.design; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.Map; +import java.util.PriorityQueue; + +/** + * FreqStack has two functions: + * + * push(int x), which pushes an integer x onto the stack. + * pop(), which removes and returns the most frequent element in the stack. + * If there is a tie for most frequent element, + * the element closest to the top of the stack is removed and returned. + * ["FreqStack","push","push","push","push","push","push","pop","pop","pop","pop"], + * [[],[5],[7],[5],[7],[4],[5],[],[],[],[]] + * Output: [null,null,null,null,null,null,null,5,7,5,4] + * Explanation: + * After making six .push operations, the stack is [5,7,5,7,4,5] from bottom to top. Then: + * + * pop() -> returns 5, as 5 is the most frequent. + * The stack becomes [5,7,5,7,4]. + * + * pop() -> returns 7, as 5 and 7 is the most frequent, but 7 is closest to the top. + * The stack becomes [5,7,5,4]. + * + * pop() -> returns 5. + * The stack becomes [5,7,4]. + * + * pop() -> returns 4. + * The stack becomes [5,7]. + */ +public class MaxFreqStack { + + PriorityQueue maxQueue; + Map hashMap; + int sequence = 0; + + public MaxFreqStack() { + + maxQueue= new PriorityQueue<>((a,b)->{ + if(a.frequency==b.frequency){ + return Integer.compare(b.sequence,a.sequence); + } + return Integer.compare(b.frequency,a.frequency); + }); + hashMap= new HashMap<>(); + } + + public void push(int x) { + hashMap.put(x, hashMap.getOrDefault(x, 0) + 1); + maxQueue.offer(new EntryStack(x, hashMap.get(x), sequence++)); + } + + public int pop() { + EntryStack temp = maxQueue.poll(); + hashMap.put(temp.val, temp.frequency - 1); + + return temp.val; + } + +} + +// 1. val = value of the number +// 2. frequency = current frequency of the number when it was pushed to the heap +// 3. sequenceNumber = a sequence number, to know what number came first +class EntryStack { + int val; + int frequency; + int sequence; + + public EntryStack(int val, int frequency, int sequence) { + this.val = val; + this.frequency = frequency; + this.sequence = sequence; + } +} + +/** + * tricky + */ +class FreqStack { + + HashMap> st ; // stores max freq as key and stack consist of all element that has freq equals to key. + + HashMap freqMap; // frequency map + int maxFreq; // will contain the max frequency of element. + + public FreqStack() { + st = new HashMap<>() ; + freqMap = new HashMap<>(); + maxFreq = 0 ; + } + + public void push(int val) { + + freqMap.put(val, freqMap.getOrDefault(val,0)+1) ; // incrementing the freq of current val + + int freq= freqMap.get(val) ; + + st.getOrDefault(freq, new LinkedList<>()).addFirst(val) ; // adding on top. + + maxFreq = Math.max(maxFreq, freqMap.get(val)) ; // updating the max frequency + } + + public int pop() { + + // getting the max freq element. + + int val = st.get(maxFreq).removeFirst() ; + + if(st.get(maxFreq).isEmpty()){ + // means stack becomes empty that means we don't have any more element of maxFreq so decrease the maxFreq. + maxFreq-- ; + } + + // also decrement the freq of val in fmap. + + int freq = freqMap.get(val) ; + + if(freq == 1){ + freqMap.remove(val) ; + } + else{ + freqMap.put(val,freq-1) ; + } + + return val ; + + } +} diff --git a/src/main/java/practiceproblems/design/MaxStack.java b/src/main/java/practiceproblems/design/MaxStack.java new file mode 100644 index 0000000..8f7dde9 --- /dev/null +++ b/src/main/java/practiceproblems/design/MaxStack.java @@ -0,0 +1,58 @@ +package practiceproblems.design; + +import java.util.ArrayDeque; +import java.util.Deque; + +public class MaxStack { + Deque stack; + Deque maxStack; + + public MaxStack() { + stack = new ArrayDeque<>(); + maxStack = new ArrayDeque<>(); + } + + public void push(int x) { + int max = maxStack.isEmpty() ? x : maxStack.peek(); + maxStack.push(Math.max(max, x)); + stack.push(x); + } + + public int pop() { + maxStack.pop(); + return stack.pop(); + } + + public int top() { + return stack.peek(); + } + + public int peekMax() { + return maxStack.peek(); + } + + public int popMax() { + int max = peekMax(); + Deque buffer = new ArrayDeque<>(); + while (top() != max) buffer.push(pop()); + pop(); + while (!buffer.isEmpty()) push(buffer.pop()); + return max; + } + + public static void main(String[] args) { + MaxStack stack = new MaxStack(); + stack.push(3); + stack.push(7); + System.out.println(stack.peekMax()); + stack.push(2); + System.out.println(stack.popMax()); + stack.push(4); + System.out.println(stack.top()); + stack.push(6); + System.out.println(stack.popMax()); + stack.push(9); + System.out.println(stack.peekMax()); + + } +} diff --git a/src/main/java/practiceproblems/design/MinimumStack.java b/src/main/java/practiceproblems/design/MinimumStack.java new file mode 100644 index 0000000..6d71616 --- /dev/null +++ b/src/main/java/practiceproblems/design/MinimumStack.java @@ -0,0 +1,43 @@ +package practiceproblems.design; + + +public class MinimumStack { + + private Node head; + + public void push(int x) { + if (head == null) { + head = new Node(x, x); + } else { + head = new Node(x, Math.min(x, head.min), head); + } + } + + public void pop() { + head = head.next; + } + + public int top() { + return head.val; + } + + public int getMin() { + return head.min; + } + + private class Node { + int val; + int min; + Node next; + + private Node(int val, int min) { + this(val, min, null); + } + + private Node(int val, int min, Node next) { + this.val = val; + this.min = min; + this.next = next; + } + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/design/MyCalendar.java b/src/main/java/practiceproblems/design/MyCalendar.java new file mode 100644 index 0000000..e818dc3 --- /dev/null +++ b/src/main/java/practiceproblems/design/MyCalendar.java @@ -0,0 +1,47 @@ +package practiceproblems.design; + +import java.util.TreeMap; + +public class MyCalendar { + TreeMap calendar; + + MyCalendar() { + calendar = new TreeMap(); + } + + /** + * floor ceiling + * ... |----| ... |----| ... + * |---------| + * s e + * if s < floor.end or e > ceiling.start, there is an overlap. + * + * Another way to think of it: + * If there is an interval start within the new book (must be the ceilingEntry) at all, or + * books: |----| |--| + * s |------| e + * + * books: |----| |----| + * s |----| e + * If the new book start within an interval (must be the floorEntry) at all + * books: |-------| |--| + * s |---| e + * + * books: |----| |----| + * s |----| e + * There is a overlap + * @param start + * @param end + * @return + */ + public boolean book(int start, int end) { + Integer prev = calendar.floorKey(start), + next = calendar.ceilingKey(start); + if ((prev == null || calendar.get(prev) <= start) && + (next == null || end <= next)) { + calendar.put(start, end); + return true; + } + return false; + } +} diff --git a/src/main/java/practiceproblems/design/MyCircularQueue.java b/src/main/java/practiceproblems/design/MyCircularQueue.java new file mode 100644 index 0000000..19a22f5 --- /dev/null +++ b/src/main/java/practiceproblems/design/MyCircularQueue.java @@ -0,0 +1,58 @@ +package practiceproblems.design; + +public class MyCircularQueue { + + int[] arr; + int headIndex = 0; + + int length; + int count; + + public MyCircularQueue(int k) { + + this.arr = new int[k]; + this.length = k; + this.count = 0; + } + + public boolean enQueue(int value) { + if (count == length) return false; + + int index = (headIndex + count) % length; + arr[index] = value; + + count++; + return true; + } + + public boolean deQueue() { + if (count == 0) return false; + + headIndex = (headIndex + 1) % length; + count -= 1; + return true; + } + + public int Front() { + if (count == 0) return -1; + return arr[headIndex]; + } + + public int Rear() { + if (count == 0) return -1; + + int tailIndex = (headIndex + count - 1) % length; + return arr[tailIndex]; + + } + + public boolean isEmpty() { + return count <= 0; + } + + public boolean isFull() { + return count == length; + } + + +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/design/OwnDataStructureUtil.java b/src/main/java/practiceproblems/design/OwnDataStructureUtil.java new file mode 100644 index 0000000..d5efef1 --- /dev/null +++ b/src/main/java/practiceproblems/design/OwnDataStructureUtil.java @@ -0,0 +1,58 @@ +package practiceproblems.design; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Random; + +/** + * https://leetcode.com/problems/insert-delete-getrandom-o1/ + */ +class OwnDataStructureUtil { + ArrayList list; + HashMap map; + Random rand = new Random(); + + public OwnDataStructureUtil() { + list = new ArrayList<>(); + map = new HashMap<>(); + } + + public boolean insert(int val) { + if (map.containsKey(val)) { + return false; + } + map.put(val, list.size()); + list.add(val); + return true; + } + + public boolean remove(int val) { + if (!map.containsKey(val)) { + return false; + } + int loc = map.get(val); + if (loc < list.size() - 1) { // not the last one than swap the last one with this val + int lastOne = list.get(list.size() - 1); + list.set(loc, lastOne); + map.put(lastOne, loc); + } + map.remove(val); + list.remove(list.size() - 1); + return true; + } + + public int getRandom() { + return list.get(rand.nextInt(list.size())); + } + + public static void main(String[] args) { + OwnDataStructureUtil ds = new OwnDataStructureUtil(); + ds.insert(10); + ds.insert(20); + ds.insert(30); + ds.insert(40); + ds.remove(20); + ds.insert(50); + System.out.println(ds.getRandom()); + } +} diff --git a/src/main/java/practiceproblems/design/RangeModule.java b/src/main/java/practiceproblems/design/RangeModule.java new file mode 100644 index 0000000..42da0f7 --- /dev/null +++ b/src/main/java/practiceproblems/design/RangeModule.java @@ -0,0 +1,65 @@ +package practiceproblems.design; + +import java.util.Iterator; +import java.util.TreeSet; + +/** + * tricky tree set + * https://leetcode.com/problems/range-module/ + */ +public class RangeModule { + TreeSet ts; + + public RangeModule() { + this.ts = new TreeSet<>((a, b) -> a[1] - b[1]); + } + + public void addRange(int left, int right) { + Iterator iter = ts.tailSet(new int[]{0, left}, true).iterator(); + while (iter.hasNext()) { + int[] temp = iter.next(); + if (temp[0] > right) break; + + left = Math.min(left, temp[0]); + right = Math.max(right, temp[1]); + iter.remove(); + } + + ts.add(new int[]{left, right}); + } + + public boolean queryRange(int left, int right) { + int[] ceiling = ts.ceiling(new int[]{0, right}); + return ceiling != null && ceiling[0] <= left; + } + + public void removeRange(int left, int right) { + Iterator iter = ts.tailSet(new int[]{0, left}, false).iterator(); + + int[] front = null; + int[] back = null; + + while (iter.hasNext()) { + int[] temp = iter.next(); + if (temp[0] >= right) break; + + if (temp[0] < left) front = new int[]{temp[0], left}; + if (right < temp[1]) back = new int[]{right, temp[1]}; + iter.remove(); + } + + if (front != null) ts.add(front); + if (back != null) ts.add(back); + } + + public static void main(String[] args) { + RangeModule rm = new RangeModule(); + rm.addRange(10, 20); + rm.addRange(11, 25); + rm.removeRange(9, 16); + rm.queryRange(10, 14); + rm.queryRange(13, 15); + rm.queryRange(16, 17); + + } +} diff --git a/src/main/java/practiceproblems/design/ShortestWordDistance.java b/src/main/java/practiceproblems/design/ShortestWordDistance.java new file mode 100644 index 0000000..cb4c3db --- /dev/null +++ b/src/main/java/practiceproblems/design/ShortestWordDistance.java @@ -0,0 +1,83 @@ +package practiceproblems.design; + +import java.util.ArrayList; +import java.util.HashMap; + +/** + * https://leetcode.com/problems/shortest-word-distance-ii + */ +public class ShortestWordDistance { + + HashMap> locations; + + public ShortestWordDistance(String[] words) { + this.locations = new HashMap<>(); + + // Prepare a mapping from a word to all it's locations (indices). + for (int i = 0; i < words.length; i++) { + ArrayList loc = this.locations.getOrDefault(words[i], new ArrayList<>()); + loc.add(i); + this.locations.put(words[i], loc); + } + } + + public int shortest(String word1, String word2) { + ArrayList loc1, loc2; + + // Location lists for both the words + // the indices will be in SORTED order by default + loc1 = this.locations.get(word1); + loc2 = this.locations.get(word2); + + int l1 = 0, l2 = 0, minDiff = Integer.MAX_VALUE; + while (l1 < loc1.size() && l2 < loc2.size()) { + minDiff = Math.min(minDiff, Math.abs(loc1.get(l1) - loc2.get(l2))); + if (loc1.get(l1) < loc2.get(l2)) { + l1++; + } else { + l2++; + } + } + + return minDiff; + } + + /** + * https://leetcode.com/problems/shortest-word-distance-iii + */ + public int shortestWordDistance(String[] words, String word1, String word2) { + int min = Integer.MAX_VALUE; + int p1 = -1; + int p2 = -1; + boolean same = word1.equals(word2); + for (int i = 0; i < words.length; i++) { + if (words[i].equals(word1)) { + if (same) { + p2 = p1; // Deal with another pointer too + } + p1 = i; + } else if (words[i].equals(word2)) { + p2 = i; + } + + if (p1 != -1 && p2 != -1) { + min = Math.min(min, Math.abs(p1 - p2)); + } + } + return min; + } + + public int shortestDistance(String[] words, String word1, String word2) { + int ret = Integer.MAX_VALUE, index1 = -1, index2 = -1; + for (int i = 0; i < words.length; i++) { + if (words[i].equals(word1)) { + index1 = i; + if (index2 >= 0) ret = Math.min(ret, i - index2); + } else if (words[i].equals(word2)) { + index2 = i; + if (index1 >= 0) ret = Math.min(ret, i - index1); + } + } + return ret; + } +} diff --git a/src/main/java/practiceproblems/design/SnakeGame.java b/src/main/java/practiceproblems/design/SnakeGame.java new file mode 100644 index 0000000..90a1b10 --- /dev/null +++ b/src/main/java/practiceproblems/design/SnakeGame.java @@ -0,0 +1,87 @@ +package practiceproblems.design; + +import java.util.LinkedList; + +/** + * tricky Linkedlist + */ +public class SnakeGame { + + // used to verify boundary conditions + int width; + int height; + + // food position + int[][] food; + int score; + + // body of snake + LinkedList snake; + + public SnakeGame(int width, int height, int[][] food) { + this.width = width; + this.height = height; + this.food = food; + + snake = new LinkedList<>(); + snake.add(new Node(0, 0)); + } + + + public int move(String direction) { + Node head = snake.peekFirst(); + Node nxt = new Node(head.x, head.y); + + switch (direction) { + case "U": + nxt.x--; + break; + case "D": + nxt.x++; + break; + case "L": + nxt.y--; + break; + case "R": + nxt.y++; + } + + // boundary check + if (nxt.x < 0 || nxt.x >= height || nxt.y < 0 || nxt.y >= width) + return -1; + + // body check -> check size()-1 as the last node will be cleared(when snake moves) if moved to next block + for (int i = 0; i < snake.size() - 1; i++) { + if (nxt.isEqual(snake.get(i))) { + return -1; + } + } + + // is food? Eat! + if (score < food.length && nxt.x == food[score][0] && nxt.y == food[score][1]) { + score++; + snake.addFirst(nxt); + } else { + snake.addFirst(nxt); + snake.removeLast(); + } + + return score; + } + + + static class Node { + int x; + int y; + + Node(int x, int y) { + this.x = x; + this.y = y; + } + + public boolean isEqual(Node node) { + return this.x == node.x && this.y == node.y; + } + } + +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/design/SnapShotUtil.java b/src/main/java/practiceproblems/design/SnapShotUtil.java new file mode 100644 index 0000000..0b45c37 --- /dev/null +++ b/src/main/java/practiceproblems/design/SnapShotUtil.java @@ -0,0 +1,72 @@ +package practiceproblems.design; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.TreeMap; +import java.util.stream.IntStream; + +/** + * https://leetcode.com/problems/snapshot-array/ + */ +class SnapShotUtil { + + int[] arr; + List snapShot; + int maxIndex = 0; + + /** + * initializes an array-like data structure with the given length. Initially, each element equals 0. + */ + public SnapShotUtil(int length) { + arr = new int[length]; + snapShot = new ArrayList<>(); + } + + /** + * sets the element at the given index to be equal to val. + */ + public void set(int index, int val) { + arr[index] = val; + maxIndex = Math.max(index, maxIndex); + } + + /** + * takes a snapshot of the array and returns the snap_id: the total number of times we called snap() minus 1. + */ + public int snap() { + snapShot.add(Arrays.copyOfRange(arr, 0, maxIndex + 1)); + return snapShot.size() - 1; + } + + /** + * returns the value at the given index, at the time we took the snapshot with the given snap_id + */ + public int get(int index, int snap_id) { + int[] arr = snapShot.get(snap_id); + if (index >= arr.length) return 0; + return arr[index]; + } + + + private int count; + private List> shot = new ArrayList<>(); + + public SnapShotUtil(int length, int k) { + IntStream.range(0, length).forEach(i -> shot.add(new TreeMap<>())); + } + + public void setOptimised(int index, int val) { + shot.get(index).put(count, val); + } + + public int snapOptimised() { + return count++; + } + + public int getOptimised(int index, int snap_id) { + Integer key = shot.get(index).floorKey(snap_id); + return key == null ? 0 : shot.get(index).get(key); + } +} + diff --git a/src/main/java/practiceproblems/design/StockPrice.java b/src/main/java/practiceproblems/design/StockPrice.java new file mode 100644 index 0000000..71bd65d --- /dev/null +++ b/src/main/java/practiceproblems/design/StockPrice.java @@ -0,0 +1,77 @@ +package practiceproblems.design; + +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.PriorityQueue; + +/** + * https://leetcode.com/problems/stock-price-fluctuation/ + */ +public class StockPrice { + static class Node { + public int timestamp; + public int price; + + public Node(int timestamp, int price) { + this.timestamp = timestamp; + this.price = price; + } + } + + Map timeToPrice = new HashMap<>(); + + PriorityQueue maxHeap = new PriorityQueue<>((a, b) -> Integer.compare(b.price, a.price)); + + PriorityQueue minHeap = new PriorityQueue<>(Comparator.comparingInt(a -> a.price)); + + Node latest = null; + + public StockPrice() { + + } + + /** + * Updates the price of the stock at the given timestamp. + */ + public void update(int timestamp, int price) { + Node newNode = new Node(timestamp, price); + timeToPrice.put(timestamp, newNode); + maxHeap.add(newNode); + minHeap.add(newNode); + if (latest == null || timestamp >= latest.timestamp) { + latest = newNode; + } + } + + /** + * Returns the latest price of the stock. + */ + public int current() { + return latest.price; + } + + /** + * Returns the maximum price of the stock. + */ + public int maximum() { + if (maxHeap.isEmpty()) return -1; + + while (maxHeap.peek().price != timeToPrice.get(maxHeap.peek().timestamp).price) { + maxHeap.remove(); + } + return maxHeap.isEmpty() ? -1 : maxHeap.peek().price; + } + + /** + * Returns the minimum price of the stock. + */ + public int minimum() { + if (minHeap.isEmpty()) return -1; + + while (minHeap.peek().price != timeToPrice.get(minHeap.peek().timestamp).price) { + minHeap.remove(); + } + return minHeap.isEmpty() ? -1 : minHeap.peek().price; + } +} diff --git a/src/main/java/practiceproblems/design/SubRectangleQueries.java b/src/main/java/practiceproblems/design/SubRectangleQueries.java new file mode 100644 index 0000000..252f198 --- /dev/null +++ b/src/main/java/practiceproblems/design/SubRectangleQueries.java @@ -0,0 +1,37 @@ +package practiceproblems.design; + +import java.util.ArrayList; +import java.util.List; + +/** + * https://leetcode.com/problems/subrectangle-queries/ + */ +public class SubRectangleQueries { + + + private int[][] rec = null; + private List list = null; + + public SubRectangleQueries(int[][] rectangle) { + rec = rectangle; + list = new ArrayList<>(); + + } + + public void updateSubrectangle(int row1, int col1, int row2, int col2, int newValue) { + int[] arr = new int[]{row1, col1, row2, col2, newValue}; + list.add(arr); + } + + public int getValue(int row, int col) { + int result = rec[row][col]; + + for (int[] arr : list) { + if (row >= arr[0] && row <= arr[2] && col <= arr[3] && col >= arr[1]) { + result = arr[4]; + } + } + + return result; + } +} diff --git a/src/main/java/practiceproblems/design/SummaryRanges.java b/src/main/java/practiceproblems/design/SummaryRanges.java new file mode 100644 index 0000000..c642139 --- /dev/null +++ b/src/main/java/practiceproblems/design/SummaryRanges.java @@ -0,0 +1,88 @@ +package practiceproblems.design; + +import java.util.TreeMap; + +/** + * https://leetcode.com/problems/data-stream-as-disjoint-intervals + * + * // When do we merge the number to an existing interval? + * // 1. If it's within 1 interval(No merge operation needed in this case) + * // 2. If it's 1 less than left bound or 1 larger than right bound of 1 interval + */ +public class SummaryRanges { + TreeMap sortedMap; + + /** + * Initialize your data structure here. + */ + public SummaryRanges() { + sortedMap = new TreeMap<>(); + } + + public void addNum(int val) { + if (sortedMap.containsKey(val)) return; + + Integer floor = sortedMap.lowerKey(val); + Integer ceiling = sortedMap.higherKey(val); + + /* + When both ceiling and floor are there + for example 1,1 and 3,3 are there and val=2 comes + sortedMap.get(floor).end + 1 == val => 1+1 == 2 + ceiling - 1 == val => 3-1 ==2 + in this case remove 3 and update 1,1's end as 1,3 + */ + if (floor != null && ceiling != null && sortedMap.get(floor).end + 1 == val && ceiling - 1 == val) { + + sortedMap.get(floor).end = sortedMap.get(ceiling).end; + sortedMap.remove(ceiling); + + } else if (floor != null && val <= sortedMap.get(floor).end + 1) { + + /* + * When ceiling is null or ceiling - 1 != val + * for ex 1 => [1,4] and val = 5 comes + * or + * 1 => [1,6] and val = 5 comes + */ + sortedMap.get(floor).end = Math.max(sortedMap.get(floor).end, val); + + } else if (ceiling != null && ceiling - 1 == val) { + + /* + * when [7,7] is there in the map and val=6 comes + * ceiling =7 and val =6 + * update the val inside the map and remove the ceiling + */ + sortedMap.put(val, new Interval(val, sortedMap.get(ceiling).end)); + sortedMap.remove(ceiling); + + } else { + /* + When both ceiling and floor are not there + */ + sortedMap.put(val, new Interval(val, val)); + } + } + + public int[][] getIntervals() { + int[][] result = new int[sortedMap.size()][2]; + int i = 0; + for (Integer key : sortedMap.keySet()) { + result[i] = new int[]{sortedMap.get(key).start, sortedMap.get(key).end}; + i++; + } + + return result; + } + + private static class Interval { + int start; + int end; + + public Interval(int start, int end) { + this.start = start; + this.end = end; + } + } +} diff --git a/src/main/java/practiceproblems/design/TimeMap.java b/src/main/java/practiceproblems/design/TimeMap.java new file mode 100644 index 0000000..685ffb2 --- /dev/null +++ b/src/main/java/practiceproblems/design/TimeMap.java @@ -0,0 +1,63 @@ +package practiceproblems.design; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Create a time based key-value store class TimeMap, that supports two operations. + *

+ * 1. set(string key, string value, int timestamp) + *

+ * Stores the key and value, along with the given timestamp. + * 2. get(string key, int timestamp) + *

+ * Returns a value such that set(key, value, timestamp_prev) was called previously, + * with timestamp_prev <= timestamp. + * If there are multiple such values, it returns the one with the largest timestamp_prev. + * If there are no values, it returns the empty string (""). + *

+ * Input: inputs = ["TimeMap","set","get","get","set","get","get"], inputs = [[],["foo","bar",1],["foo",1],["foo",3],["foo","bar2",4],["foo",4],["foo",5]] + * Output: [null,null,"bar","bar",null,"bar2","bar2"] + * Explanation: + * TimeMap kv; + * kv.set("foo", "bar", 1); // store the key "foo" and value "bar" along with timestamp = 1 + * kv.get("foo", 1); // output "bar" + * kv.get("foo", 3); // output "bar" since there is no value corresponding to foo at timestamp 3 and timestamp 2, then the only value is at timestamp 1 ie "bar" + * kv.set("foo", "bar2", 4); + * kv.get("foo", 4); // output "bar2" + * kv.get("foo", 5); //output "bar2" + */ + +class TimeMap { + + class Value { + String val; + int time; + + public Value(String val, int time) { + this.val = val; + this.time = time; + } + } + + Map> map; + + public TimeMap() { + map = new HashMap<>(); + } + + public void set(String key, String value, int timestamp) { + map.computeIfAbsent(key, x->new ArrayList<>()).add(new Value(value, timestamp)); + } + + public String get(String key, int timestamp) { + if (!map.containsKey(key)) return ""; + List values = map.get(key); + for (int i = values.size() - 1; i >= 0; i--) { + if (timestamp >= values.get(i).time) return values.get(i).val; + } + return ""; + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/design/TweetCounts.java b/src/main/java/practiceproblems/design/TweetCounts.java new file mode 100644 index 0000000..ac10ae9 --- /dev/null +++ b/src/main/java/practiceproblems/design/TweetCounts.java @@ -0,0 +1,60 @@ +package practiceproblems.design; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * https://leetcode.com/problems/tweet-counts-per-frequency/ + * + * tricky tree map + */ +class TweetCounts { + private Map> tweetsCache; + + public TweetCounts() { + tweetsCache = new HashMap<>(); + } + + public void recordTweet(String tweetName, int time) { + + tweetsCache.putIfAbsent(tweetName, new TreeMap<>()); + + TreeMap timeToFreq = tweetsCache.get(tweetName); + timeToFreq.put(time, timeToFreq.getOrDefault(time, 0) + 1); + + } + + public List getTweetCountsPerFrequency(String freq, String tweetName, int startTime, int endTime) { + int interval = 1; + if (!tweetsCache.containsKey(tweetName)) { + return Collections.emptyList(); + } + if (freq.equals("minute")) { + interval = 60; + } + if (freq.equals("hour")) { + interval = 3600; + } + if (freq.equals("day")) { + interval = 86400; + } + + int size = ((endTime - startTime) / interval) + 1; + int[] cur = new int[size]; + TreeMap temp = tweetsCache.get(tweetName); + for (Map.Entry entry : temp.subMap(startTime, endTime + 1).entrySet()) { + int pos = (entry.getKey() - startTime) / interval; + cur[pos] += entry.getValue(); + } + List res = new ArrayList<>(); + for (int num : cur) { + res.add(num); + } + + return res; + } +} diff --git a/src/geeksforgeeks/Twitter.java b/src/main/java/practiceproblems/design/Twitter.java similarity index 62% rename from src/geeksforgeeks/Twitter.java rename to src/main/java/practiceproblems/design/Twitter.java index 7441e4e..4dcff5d 100644 --- a/src/geeksforgeeks/Twitter.java +++ b/src/main/java/practiceproblems/design/Twitter.java @@ -1,11 +1,13 @@ -package geeksforgeeks; +package practiceproblems.design; import java.util.*; -/*https://leetcode.com/problems/design-twitter/discuss/82825/Java-OO-Design-with-most-efficient-function-getNewsFeed*/ +/** + * https://leetcode.com/problems/design-twitter/discuss/82825/Java-OO-Design-with-most-efficient-function-getNewsFeed + * + **/ public class Twitter { private static int timeStamp = 0; - private Map userMap; // Tweet link to next Tweet so that we can save a lot of time @@ -24,34 +26,32 @@ public Tweet(int id) { public class User { public int id; - public Set followed; - public Tweet tweet_head; + public Set followers; + public Tweet tweetHead; public User(int id) { this.id = id; - followed = new HashSet<>(); - follow(id); // first follow itself - tweet_head = null; + followers = new HashSet<>(); + follow(id); // first follow yourself + tweetHead = null; } public void follow(int id) { - followed.add(id); + followers.add(id); } public void unfollow(int id) { - followed.remove(id); + followers.remove(id); } - // everytime user post a new tweet, add it to the head of tweet list. public void post(int id) { Tweet t = new Tweet(id); - t.next = tweet_head; - tweet_head = t; + t.next = tweetHead; + tweetHead = t; } } - /** * Initialize your data structure here. */ @@ -63,45 +63,41 @@ public Twitter() { * Compose a new tweet. */ public void postTweet(int userId, int tweetId) { - if (!userMap.containsKey(userId)) { - User u = new User(userId); - userMap.put(userId, u); - } - userMap.get(userId).post(tweetId); - + userMap.computeIfAbsent(userId, x -> new User(userId)).post(tweetId); } - // Best part of this. // first get all tweets lists from one user including itself and all people it followed. // Second add all heads into a max heap. Every time we poll a tweet with - // largest time stamp from the heap, then we add its next tweet into the heap. + // the largest time stamp from the heap, then we add its next tweet into the heap. // So after adding all heads we only need to add 9 tweets at most into this // heap before we get the 10 most recent tweet. public List getNewsFeed(int userId) { - List res = new LinkedList<>(); + List result = new LinkedList<>(); - if (!userMap.containsKey(userId)) return res; + if (!userMap.containsKey(userId)) { + return result; + } - Set users = userMap.get(userId).followed; - PriorityQueue q = new PriorityQueue(users.size(), (a, b) -> (b.time - a.time)); + Set users = userMap.get(userId).followers; + PriorityQueue q = new PriorityQueue<>(users.size(), (a, b) -> (b.time - a.time)); for (int user : users) { - Tweet t = userMap.get(user).tweet_head; - // very imporant! If we add null to the head we are screwed. + Tweet t = userMap.get(user).tweetHead; + // very important! If we add null to the head we are screwed. if (t != null) { q.add(t); } } - int n = 0; - while (!q.isEmpty() && n < 10) { + + while (!q.isEmpty() && result.size()<10) { Tweet t = q.poll(); - res.add(t.id); - n++; - if (t.next != null) + result.add(t.id); + if (t.next != null) { q.add(t.next); + } } - return res; + return result; } @@ -109,14 +105,8 @@ public List getNewsFeed(int userId) { * Follower follows a followee. If the operation is invalid, it should be a no-op. */ public void follow(int followerId, int followeeId) { - if (!userMap.containsKey(followerId)) { - User u = new User(followerId); - userMap.put(followerId, u); - } - if (!userMap.containsKey(followeeId)) { - User u = new User(followeeId); - userMap.put(followeeId, u); - } + userMap.computeIfAbsent(followerId, x -> new User(followerId)); + userMap.computeIfAbsent(followeeId, x -> new User(followeeId)); userMap.get(followerId).follow(followeeId); } @@ -124,8 +114,9 @@ public void follow(int followerId, int followeeId) { * Follower unfollows a followee. If the operation is invalid, it should be a no-op. */ public void unfollow(int followerId, int followeeId) { - if (!userMap.containsKey(followerId) || followerId == followeeId) + if (!userMap.containsKey(followerId) || followerId == followeeId) { return; + } userMap.get(followerId).unfollow(followeeId); } diff --git a/src/main/java/practiceproblems/design/TwoSum.java b/src/main/java/practiceproblems/design/TwoSum.java new file mode 100644 index 0000000..dd02370 --- /dev/null +++ b/src/main/java/practiceproblems/design/TwoSum.java @@ -0,0 +1,56 @@ +package practiceproblems.design; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class TwoSum { + + // all numbers + private List list = new ArrayList<>(); + + // every number's occurrence + private Map map = new HashMap<>(); + + // record max and minimum value + int max, min; + + /** + * Initialize your data structure here. + */ + public TwoSum() { + } + + /** + * Add the number to an internal data structure.. + */ + public void add(int number) { + list.add(number); + + min = Math.min(number, min); + max = Math.max(number, max); + + map.put(number, map.getOrDefault(number, 0) + 1); + } + + /** + * Find if there exists any pair of numbers which sum is equal to the value. + */ + public boolean find(int value) { + if (value < 2 * min || value > 2 * max) { + return false; + } + + for (int num : list) { + int num2 = value - num; + + if ((num == num2 && map.getOrDefault(num, 0) > 1) || + (num != num2 && map.containsKey(num2))) { + return true; + } + } + + return false; + } +} diff --git a/src/main/java/practiceproblems/design/UndergroundSystem.java b/src/main/java/practiceproblems/design/UndergroundSystem.java new file mode 100644 index 0000000..27586d7 --- /dev/null +++ b/src/main/java/practiceproblems/design/UndergroundSystem.java @@ -0,0 +1,91 @@ +package practiceproblems.design; + +import java.util.HashMap; +import java.util.Map; + +/** + * https://leetcode.com/problems/design-underground-system/ + * + * tricky + */ +public class UndergroundSystem { + + Map passengerDetails; + Map routeDetails; + + public UndergroundSystem() { + this.passengerDetails = new HashMap<>(); + this.routeDetails = new HashMap<>(); + } + + public void checkIn(int id, String stationName, int t) { + passengerDetails.putIfAbsent(id, new Passenger(id)); + passengerDetails.get(id).checkin(stationName, t); + } + + public void checkOut(int id, String stationName, int t) { + if (!passengerDetails.containsKey(id)) return; + + Passenger pass = passengerDetails.get(id); + pass.checkout(stationName, t); + + String routeKey = pass.startStation + " , " + pass.endStation; + routeDetails.putIfAbsent(routeKey, new Route(pass.startStation, pass.endStation)); + + routeDetails.get(routeKey).addTotalTiming(pass.startTime, pass.endTime); + passengerDetails.remove(id); + } + + public double getAverageTime(String startStation, String endStation) { + return routeDetails.get(startStation + " , " + endStation).avgTimeTaken(); + } + + + static class Passenger { + + int passengerId; + String startStation; + String endStation; + int startTime; + int endTime; + + public Passenger(int passengerId) { + this.passengerId = passengerId; + } + + public void checkin(String startStation, int startTime) { + this.startStation = startStation; + this.startTime = startTime; + } + + public void checkout(String endStation, int endTime) { + this.endStation = endStation; + this.endTime = endTime; + } + } + + static class Route { + String startStation; + String endStation; + long totalTravelTime; + int numberOfTravels; + + public Route(String startStation, String endStation) { + this.startStation = startStation; + this.endStation = endStation; + this.totalTravelTime = 0; + this.numberOfTravels = 0; + } + + public void addTotalTiming(int startTime, int endTime) { + this.totalTravelTime += (endTime - startTime); + numberOfTravels++; + } + + public double avgTimeTaken() { + return (double) totalTravelTime / numberOfTravels; + } + + } +} + diff --git a/src/main/java/practiceproblems/design/UniqueCharactersInSubString.java b/src/main/java/practiceproblems/design/UniqueCharactersInSubString.java new file mode 100644 index 0000000..ef896a1 --- /dev/null +++ b/src/main/java/practiceproblems/design/UniqueCharactersInSubString.java @@ -0,0 +1,78 @@ +package practiceproblems.design; + +import java.util.*; + + +/** + * https://leetcode.com/problems/count-unique-characters-of-all-substrings-of-a-given-string/ + *

+ * https://www.youtube.com/watch?v=JT1NDR-M_8A&t=1212s + */ +public class UniqueCharactersInSubString { + + public int uniqueLetterString(String s) { + + Map> cache = new HashMap<>(); + + for (int i = 0; i < 26; i++) { + cache.putIfAbsent(i, new ArrayList<>()); + cache.get(i).add(-1); + } + + for (int i = 0; i < s.length(); i++) { + cache.get(s.charAt(i) - 'A').add(i); + } + + for (int i = 0; i < 26; i++) { + cache.get(i).add(s.length()); + } + + int result = 0; + for (int i = 0; i < 26; i++) { + for (int j = 1; j < cache.get(i).size() - 1; j++) { + result += (cache.get(i).get(j) - cache.get(i).get(j - 1)) * (cache.get(i).get(j + 1) - cache.get(i).get(j)); + } + } + + return result; + + } + + /** + * In the first for loop, we initialize the index array to -1 to indicate that none of the characters have occurred yet. + + * In the second for loop, we iterate over the input string "ABC". For each character, + * we calculate the contribution of all substrings that end with that character by using the last two occurrences of that character in the index array. + * For example, when we process "A" (at index 0), the last two occurrences of "A" are both -1, + * so the contribution of all substrings that end with "A" is (0 - (-1)) * ((-1) - (-1)) = 1. When we process "B" (at index 1), + * the last two occurrences of "B" are -1 and -1, so the contribution of all substrings that end with "B" is (1 - (-1)) * ((-1) - (-1)) = 0. + * When we process "C" (at index 2), the last two occurrences of "C" are both -1, so the contribution of all substrings that end with "C" is (2 - (-1)) * ((-1) - (-1)) = 0. + * The total contribution of all substrings is 1 + 0 + 0 = 1, so res is updated to 1. + + * In the third for loop, we calculate the contribution of all substrings that end with a character that has not occurred twice yet (i.e., all remaining characters in the index array). + * For example, for the character "D" (with index 3), the last two occurrences of "D" are both -1, so the contribution of all substrings that end with "D" is (3 - (-1)) * ((-1) - (-1)) = 0. + * We do this for all characters that have not occurred twice yet. + + * Finally, we return the result res, which is the sum of the contributions of all substrings that end with each character in the input string. For the input string "ABC", the result should be 10. + */ + public int uniqueLetterStringOptimised(String s) { + int n = s.length(); // Get the length of the input string, n = 3 for "ABC" + int res = 0; // Initialize the result variable, res = 0 + int[][] index = new int[26][2]; // Create a 2D array of size 26x2 to store the last two occurrences of each character + for (int i = 0; i < 26; i++) { + Arrays.fill(index[i], -1); // Initialize the array to -1 to indicate that the character has not occurred yet + } + for (int i = 0; i < n; i++) { + int c = s.charAt(i) - 'A'; // Get the index of the current character in the index array, c = 0 for "A", c = 1 for "B", c = 2 for "C" + res = (res + (i - index[c][1]) * (index[c][1] - index[c][0]) % 1000000007) % 1000000007; // Calculate the contribution of all substrings that end with the current character + index[c][0] = index[c][1]; // Update the last occurrence of the current character to be the second last occurrence + index[c][1] = i; // Update the last occurrence of the current character to be the current index + } + for (int i = 0; i < 26; i++) { + res = (res + (n - index[i][1]) * (index[i][1] - index[i][0]) % 1000000007) % 1000000007; // Calculate the contribution of all substrings that end with the current character + } + return res; // Return the result + } + + +} diff --git a/src/main/java/practiceproblems/design/ValidTicTacToeState.java b/src/main/java/practiceproblems/design/ValidTicTacToeState.java new file mode 100644 index 0000000..fc315ae --- /dev/null +++ b/src/main/java/practiceproblems/design/ValidTicTacToeState.java @@ -0,0 +1,58 @@ +package practiceproblems.design; + +/** + * TODO- revision + * https://leetcode.com/problems/valid-tic-tac-toe-state + */ +public class ValidTicTacToeState { + public boolean validTicTacToe(String[] board) { + // turns = 0 represents 'X' will move, otherwise, 'O' will move + int turns = 0; + + // check whether 'X' wins or 'O' wins, or no players win + boolean xWin = getWinCombination(board, 'X'); + boolean yWin = getWinCombination(board, 'O'); + + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 3; j++) { + if (board[i].charAt(j) == 'X') turns++; + if (board[i].charAt(j) == 'O') turns--; + } + } + + /** + * Four conditions will be the invalid tic tac toe board: + * 1. there are more 'O' than 'X' + * 2. the board has 2 more 'X' than 'O' + * 3. number of 'X' is equal to number of 'O', but 'X' wins, it is impossible because if 'X' wins, the game is + over, 'O' cannot play again, then number of 'O' MUST less than 'X' + * 4. number of 'X' is more than number of 'O', but 'O' wins, it is impossible because if 'O' wins, the game is + over, 'X' cannot play again, then number of 'X' CANNOT greater than 'O' + * */ + return turns >= 0 && turns <= 1 && (turns != 0 || !xWin) && (turns != 1 || !yWin); + } + + public boolean getWinCombination(String[] board, char target) { + + // check horizontal + for (int i = 0; i < 3; i++) { + if (board[i].charAt(0) == target && board[i].charAt(0) == board[i].charAt(1) && board[i].charAt(1) == board[i].charAt(2)) { + return true; + } + } + // check vertical + for (int j = 0; j < 3; j++) { + if (board[0].charAt(j) == target && board[0].charAt(j) == board[1].charAt(j) && board[1].charAt(j) == board[2].charAt(j)) { + return true; + } + } + + // check diagonal + if (board[0].charAt(0) == target && board[0].charAt(0) == board[1].charAt(1) && board[1].charAt(1) == board[2].charAt(2)) { + return true; + } + // check diagonal + return board[2].charAt(0) == target && board[2].charAt(0) == board[1].charAt(1) && board[1].charAt(1) == board[0].charAt(2); + + } +} diff --git a/src/main/java/practiceproblems/intervals/BalloonBurst.java b/src/main/java/practiceproblems/intervals/BalloonBurst.java new file mode 100644 index 0000000..67a20a6 --- /dev/null +++ b/src/main/java/practiceproblems/intervals/BalloonBurst.java @@ -0,0 +1,55 @@ +package practiceproblems.intervals; + +import java.util.Arrays; +import java.util.Comparator; + +/** + * https://leetcode.com/problems/minimum-number-of-arrows-to-burst-balloons/ + * + * + * We know that eventually we have to shoot down every balloon, + * so for each ballon there must be an arrow whose position is between balloon[0] and balloon[1] inclusively. + * Given that, we can sort the array of balloons by their ending position. + * Then we make sure that while we take care of each balloon in order, + * we can shoot as many following balloons as possible. + * + * So what position should we pick each time? + * We should shoot as to the right as possible, because since balloons are sorted, this gives you the best chance to take down more balloons. + * Therefore the position should always be balloon[i][1] for the ith balloon. + * + * This is exactly what I do in the for loop: + * check how many balloons I can shoot down with one shot aiming at the ending position of the current balloon. + * Then I skip all these balloons and start again from the next one (or the leftmost remaining one) that needs another arrow. + * + * Example: + * balloons = [[7,10], [1,5], [3,6], [2,4], [1,4]] + * After sorting, it becomes: + * balloons = [[2,4], [1,4], [1,5], [3,6], [7,10]] + * So first of all, we shoot at position 4, + * we go through the array and see that all first 4 balloons can be taken care of by this single shot. + * Then we need another shot for one last balloon. So the result should be 2. + */ +public class BalloonBurst { + + public int findMinArrowShots(int[][] points) { + if (points.length == 0) { + return 0; + } + Arrays.sort(points, Comparator.comparingInt(a -> a[1])); + + int count=1; + + int arrowPos = points[0][1]; + + for(int i=1;i=points[i][0]) + continue; + + arrowPos = points[i][1]; + count++; + } + + return count; + } +} diff --git a/src/main/java/practiceproblems/intervals/InsertIntervals.java b/src/main/java/practiceproblems/intervals/InsertIntervals.java new file mode 100644 index 0000000..0c14ceb --- /dev/null +++ b/src/main/java/practiceproblems/intervals/InsertIntervals.java @@ -0,0 +1,80 @@ +package practiceproblems.intervals; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * https://leetcode.com/problems/insert-interval/ + * + * Given a set of non-overlapping intervals, insert a new interval into the intervals (merge if necessary). + *

+ * You may assume that the intervals were initially sorted according to their start times. + * 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]. + */ +public class InsertIntervals { + public int[][] insert(int[][] intervals, int[] newInterval) { + List result = new ArrayList<>(); + int i = 0; + int n = intervals.length; + + //Add to the output all the intervals starting before newInterval. + while (i < n && intervals[i][1] < newInterval[0]) { + result.add(intervals[i++]); + } + + //Add to the output newInterval. + // Merge it with the last added interval if newInterval starts before the last added interval. + int[] mI = newInterval; + while (i < n && intervals[i][0] <= newInterval[1]) { + mI[0] = Math.min(intervals[i][0], mI[0]); + mI[1] = Math.max(intervals[i][1], mI[1]); + i++; + } + result.add(mI); + while (i < n) { + result.add(intervals[i++]); + } + + return result.toArray(new int[result.size() - 1][2]); + } + + public static int[][] insertEff(int[][] intervals, int[] newInterval) { + + List result = new ArrayList<>(); + + // Iterate through all slots + for (int[] interval : intervals) { + + // if newInterval before interval insert newInterval & update slot as new interval + if (newInterval[1] < interval[0]) { + result.add(newInterval); + newInterval = interval; + } + + // if interval is lesser than new Interval insert slot + else if (interval[1] < newInterval[0]) { + result.add(interval); + } + + // if above conditions fail it's an overlap since possibility of new interval existing in left & right of interval is checked + // update lowest of start & highest of end & not insert + else { + newInterval[0] = Math.min(newInterval[0], interval[0]); + newInterval[1] = Math.max(newInterval[1], interval[1]); + } + } + + // insert the last newInterval + result.add(newInterval); + + // convert to int[][] array + return result.toArray(new int[result.size()][]); + } + + public static void main(String[] args) { + System.out.println(Arrays.deepToString(insertEff(new int[][]{{1, 2}, {3, 5}, {6, 7}, {8, 10}, {12, 16}}, new int[]{4, 8}))); + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/intervals/MaxProfitJobScheduling.java b/src/main/java/practiceproblems/intervals/MaxProfitJobScheduling.java new file mode 100644 index 0000000..4a9bba6 --- /dev/null +++ b/src/main/java/practiceproblems/intervals/MaxProfitJobScheduling.java @@ -0,0 +1,112 @@ +package practiceproblems.intervals; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.TreeMap; + +/** + * tricky interval + * https://leetcode.com/problems/maximum-profit-in-job-scheduling + * + * https://www.youtube.com/watch?v=ZOP43iB_E_8&ab_channel=CodingDecoded + * + * So the goal is to find the profit up until the current start time and add the current profit to it, + * so we use the TreeMap to store the endTime and profit, if we pass the current startTime and find the floorKey + * from the map we will get the profit before the start of current startTime + */ +public class MaxProfitJobScheduling { + public int jobScheduling(int[] startTime, int[] endTime, int[] profit) { + Interval[] intervals = new Interval[profit.length]; + + for (int i = 0; i < profit.length; i++) { + intervals[i] = new Interval(startTime[i], endTime[i], profit[i]); + } + // sort by endTime greedily + Arrays.sort(intervals, Comparator.comparingInt(a -> a.endTime)); + TreeMap treeMap = new TreeMap<>(); + int result = Integer.MIN_VALUE; + + for (Interval interval : intervals) { + Integer key = treeMap.floorKey(interval.startTime); // returns endTime which is just before the curr start + result = Math.max(result, interval.profit + (key == null ? 0 : treeMap.get(key))); + treeMap.put(interval.endTime, result); + } + + return result; + } + + /** + * Thushar roy's approach + */ + public int jobSchedulingEff(int[] st, int[] et, int[] profit) { + int n = profit.length; + Interval[] jobs = new Interval[n]; + for (int i = 0; i < n; i++) + jobs[i] = new Interval(st[i], et[i], profit[i]); + Arrays.sort(jobs, Comparator.comparingInt(a -> a.endTime)); + + int[] dp = new int[n]; + dp[0] = jobs[0].profit; + for (int i = 1; i < n; i++) { + dp[i] = jobs[i].profit; + // we can further optimise the second for loop with binary search since the arr is sorted + for (int j = i - 1; j >= 0; j--) { + if (jobs[j].endTime <= jobs[i].startTime) { + dp[i] = Math.max(dp[i], jobs[i].profit + dp[j]); + break; + } + } + dp[i] = Math.max(dp[i], dp[i - 1]); + } + /** + * for (int i = 1; i < n; i++) { + * int profit = jobs[i].profit; + * int l = search(jobs, i); + * if (l != -1) + * profit += dp[l]; + * // Store maximum of including and excluding + * dp[i] = Math.max(profit, dp[i-1]); + * } + */ + return dp[n - 1]; + } + + // binary search code + private int search(Interval[] jobs, int index) { + int start = 0, end = index - 1; + while (start <= end) { + int mid = (start + end) / 2; + if (jobs[mid].endTime <= jobs[index].startTime) { + if (jobs[mid + 1].endTime <= jobs[index].startTime) + start = mid + 1; + else + return mid; + } else + end = mid - 1; + } + return -1; + } + + + private static class Interval { + int startTime; + int endTime; + int profit; + + public Interval(int startTime, int endTime, int profit) { + this.startTime = startTime; + this.endTime = endTime; + this.profit = profit; + } + + @Override + public String toString() { + return "Interval{" + + "startTime=" + startTime + + ", endTime=" + endTime + + ", profit=" + profit + + '}'; + } + + } +} diff --git a/src/main/java/practiceproblems/intervals/MeetingRoomsII.java b/src/main/java/practiceproblems/intervals/MeetingRoomsII.java new file mode 100644 index 0000000..f1c3419 --- /dev/null +++ b/src/main/java/practiceproblems/intervals/MeetingRoomsII.java @@ -0,0 +1,82 @@ +package practiceproblems.intervals; + + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.PriorityQueue; + +/** + * https://www.lintcode.com/problem/meeting-rooms-ii/ + */ + +public class MeetingRoomsII { + // [(0,30),(5,10),(15,20)] + + // without using priority queue these cases will fail + // [[1,5],[8,9],[8,9]] + // [[9,10],[4,9],[4,17]] + public int minMeetingRooms(int[][] intervals) { + + Arrays.sort(intervals, Comparator.comparingInt(a -> a[0])); + + // Use a min heap to track the minimum end time of merged intervals + // this will take care of multiple same start time + PriorityQueue queue = new PriorityQueue<>(); + + int end = intervals[0][1]; + queue.offer(end); + for (int i = 1; i < intervals.length; i++) { + if (queue.peek() <= intervals[i][0]) { + queue.poll(); + } + queue.offer(intervals[i][1]); + } + + return queue.size(); + + } + + // Input: schedule = [[[1,2],[5,6]],[[1,3]],[[4,10]]] + // Output: [[3,4]] + + public List employeeFreeTime(List> schedule) { + PriorityQueue que = new PriorityQueue<>(Comparator.comparingInt(a -> a.start)); + + for (List list : schedule) { + for (Interval i : list) { + que.add(i); + } + } + + List rt = new ArrayList<>(); + int max = -1; + while (!que.isEmpty()) { + Interval top = que.poll(); + if (max != -1 && top.start > max) { + rt.add(new Interval(max, top.start)); + } + max = Math.max(max, top.end); + } + + return rt; + } + + class Interval { + + public int buy, sell; + int start; // for meeting problem + int end; + + public Interval(int buy, int sell) { + this.buy = buy; + this.sell = sell; + } + + public Interval() { + + } + } + +} diff --git a/src/main/java/practiceproblems/intervals/MergeIntervalIntersection.java b/src/main/java/practiceproblems/intervals/MergeIntervalIntersection.java new file mode 100644 index 0000000..4d9dae3 --- /dev/null +++ b/src/main/java/practiceproblems/intervals/MergeIntervalIntersection.java @@ -0,0 +1,84 @@ +package practiceproblems.intervals; + +import java.util.ArrayList; +import java.util.List; + +/** + * https://leetcode.com/problems/interval-list-intersections/ + * Return the intersection of these two interval lists. + * Input: firstList = [[0,2],[5,10],[13,23],[24,25]], secondList = [[1,5],[8,12],[15,24],[25,26]] + * + * Output: [[1,2],[5,5],[8,10],[15,23],[24,24],[25,25]] + */ +public class MergeIntervalIntersection { + + // inorder to find a overlapping part alone between two intervals + // we take + // start = max(a.start, b.start) + // end = min(a.end, b.end) + // That is, the highest start time and the lowest end time will be the overlapping interval. + public int[][] intervalIntersection(int[][] A, int[][] B) { + int i = 0; + int j = 0; + List result = new ArrayList<>(); + while (i < A.length && j < B.length) { + + if (A[i][0] >= B[j][0] && A[i][0] <= B[j][1] || + B[j][0] >= A[i][0] && B[j][0] <= A[i][1]) { // this condition checks if there's a overlapping + //A=>[0,2], B=> [1,5] + result.add(new int[]{Math.max(A[i][0], B[j][0]), Math.min(A[i][1], B[j][1])}); + } + // once added to result move the i or j based on lesser end time + if (A[i][1] < B[j][1]) { // Exhausted this range in A + i++; // # Point to next range in A + } else { //# Exhausted this range in B + j++; //# Point to next range in B + } + } + + return result.toArray(new int[result.size()][2]); + } + + public int[][] intervalIntersectionEff(int[][] A, int[][] B) { + List ans = new ArrayList<>(); + int i = 0, j = 0; + + while (i < A.length && j < B.length) { + // Let's check if A[i] intersects B[j]. + // lo - the starting point of the intersection + // hi - the endpoint of the intersection + int lo = Math.max(A[i][0], B[j][0]); + int hi = Math.min(A[i][1], B[j][1]); + if (lo <= hi) + ans.add(new int[]{lo, hi}); + + // Remove the interval with the smallest endpoint + if (A[i][1] < B[j][1]) + i++; + else + j++; + } + + return ans.toArray(new int[ans.size()][]); + } + + public int[][] intervalIntersectionBrute(int[][] A, int[][] B) { + List list = new ArrayList<>(); + for (int[] ints : A) { + for (int[] value : B) { + int[] intersect = findIntersection(ints, value); + if (intersect.length != 0) list.add(intersect); + } + } + return list.toArray(new int[list.size()][]); + } + + /** + * 1st interval ending should be greater than 2nd 's first + * 1st interval begin should be less than 2nd's end + */ + public int[] findIntersection(int[] a, int[] b) { + if (a[1] < b[0] || b[1] < a[0]) return new int[]{}; + return new int[]{Math.max(a[0], b[0]), Math.min(a[1], b[1])}; + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/intervals/MergeIntervals.java b/src/main/java/practiceproblems/intervals/MergeIntervals.java new file mode 100644 index 0000000..feaf47f --- /dev/null +++ b/src/main/java/practiceproblems/intervals/MergeIntervals.java @@ -0,0 +1,41 @@ +package practiceproblems.intervals; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +/** + * https://leetcode.com/problems/merge-intervals/ + */ +class MergeIntervals { + + public static int[][] merge(int[][] intervals) { + if (intervals.length <= 1) { + return intervals; + } + List result = new ArrayList<>(); + Arrays.sort(intervals, Comparator.comparingInt(a -> a[0])); + + for (int[] interval : intervals) { + int start = interval[0]; + int end = interval[1]; + + if (result.isEmpty() || start > result.get(result.size() - 1)[1]) { + result.add(new int[]{start, end}); + } else { + result.get(result.size() - 1)[1] = Math.max(end, result.get(result.size() - 1)[1]); + } + } + + return result.toArray(new int[result.size()][]); + } + + public static void main(String[] args) { + // [[1,3],[2,6],[8,10],[15,18]] + int[][] arr = { { 1, 9 }, { 6, 8 }, { 2, 4 }, { 4, 7 } }; + //{ { 1, 3 }, { 2, 4 }, { 5, 7 }, { 6, 8 } }; + //{{1, 9}, {2, 4}, {4, 7}, {6, 8}}; + System.out.println(Arrays.deepToString(merge(arr))); + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/intervals/OverlappingIntervals.java b/src/main/java/practiceproblems/intervals/OverlappingIntervals.java new file mode 100644 index 0000000..f8384e9 --- /dev/null +++ b/src/main/java/practiceproblems/intervals/OverlappingIntervals.java @@ -0,0 +1,61 @@ +package practiceproblems.intervals; + +import java.util.Arrays; + +/** + * https://leetcode.com/problems/non-overlapping-intervals/ + */ + +public class OverlappingIntervals { + + public int eraseOverlapIntervals(int[][] intervals) { + Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0])); + + int maxObservedEnd = intervals[0][1]; + int result = 0; + for (int i = 1; i < intervals.length; i++) { + int start = intervals[i][0]; + int end = intervals[i][1]; + + if (start < maxObservedEnd) { + result++; + /** + * The reason we choose min of ends + * 1 -------------------- + * 2 ----- ----------------- 3 + * + * Suppose we have three intervals as above, and we sort them acc to start time. + * Then for interval 2, which overlaps with 1, we can either remove 1 or 2. + * If we removed 2, then interval 3 will overlap again with 1 and we have to remove one more interval(mostly 3). + * We are removing 2 intervals which is not the correct ans. + * We need more logic to make the code work correctly. + * + * So instead of 2 if we removed 1 then we have to update the previous end point as 1 is now gone. + * Hence, the assignment of the smaller endtime (e = itv.end). + * Interval 3 doesnt overlap with last end time and so we only have to remove 1 interval, + * which is what we expected. + */ + maxObservedEnd = Math.min(maxObservedEnd, end); + } else { + maxObservedEnd = end; + } + } + + return result; + } + + public int eraseOverlappingIntervalsAlter(int[][] intervals) { + Arrays.sort(intervals, (a, b) -> (a[1] - b[1])); + + int ct = 1; + int prev = intervals[0][1]; + for (int i = 1; i < intervals.length; i++) { + if (intervals[i][0] >= prev) { + ct++; + prev = intervals[i][1]; + } + } + return intervals.length - ct; + } + +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/intervals/RectangleComputeArea.java b/src/main/java/practiceproblems/intervals/RectangleComputeArea.java new file mode 100644 index 0000000..0245fa9 --- /dev/null +++ b/src/main/java/practiceproblems/intervals/RectangleComputeArea.java @@ -0,0 +1,33 @@ +package practiceproblems.intervals; + +/** + * https://leetcode.com/problems/rectangle-area/submissions/ + * + * Calculate the area of each rectangle at first. Judge whether they have intersection. + * If not, return the sum area of them. + * Otherwise, count the intersection area and subtract it by one time of total area. + */ +public class RectangleComputeArea { + + public int computeArea(int ax1, int ay1, int ax2, int ay2, int bx1, int by1, int bx2, int by2) { + + + int rectOneArea = (ax2 - ax1) * (ay2 - ay1); + int rectTwoArea = (bx2 - bx1) * (by2 - by1); + + + int[] overlapping = new int[4]; + + overlapping[0] = Math.max(ax1, bx1); + overlapping[1] = Math.max(ay1, by1); + + overlapping[2] = Math.min(ax2, bx2); + overlapping[3] = Math.min(ay2, by2); + + int overlapLength = overlapping[2] - overlapping[0]; + int overlapWidth = overlapping[3] - overlapping[1]; + + + return rectOneArea + rectTwoArea - (overlapLength <= 0 || overlapWidth <= 0 ? 0 : overlapLength * overlapWidth); + } +} diff --git a/src/main/java/practiceproblems/intervals/RectangleOverlap.java b/src/main/java/practiceproblems/intervals/RectangleOverlap.java new file mode 100644 index 0000000..4962ba3 --- /dev/null +++ b/src/main/java/practiceproblems/intervals/RectangleOverlap.java @@ -0,0 +1,62 @@ +package practiceproblems.intervals; + +/** + * https://leetcode.com/problems/rectangle-overlap + * + * For overlapping questions: + * + * Interval A = [leftA, rightA] + * Interval B = [leftB, rightB] + * Overlapping region: [max(leftA, leftB) , min(rightA, rightB)] + * + * which means if(max(leftA, leftB) < min(rightA, rightB)), there is an overlap. + * So the code of this problem is to check whether x is overlapped && y is overlapped. + * + * + * ------------ ------------- + * 0 1 2 3 + * + * start = Math.max(start1, start2) = 2 + * end = Math.min(end1, end2) = 1 + * + * does not overlap as start > end + * + * ------------- + * 1 3 + * ------------ + * 0 2 + * + * start = Math.max(start1, start2) = 1 + * end = Math.min(end1, end2) = 2 + * + * overlap as start < end + */ +public class RectangleOverlap { + public boolean isRectangleOverlap(int[] rec1, int[] rec2) { + + int x1 = rec1[0]; + int y1 = rec1[1]; + + int x2 = rec1[2]; + int y2 = rec1[3]; + + int r2X1 = rec2[0]; + int r2Y1 = rec2[1]; + + int r2X2 = rec2[2]; + int r2Y2 = rec2[3]; + + boolean overlapX = false; + boolean overlapY = false; + + if (Math.max(x1, r2X1) < Math.min(x2, r2X2)) { + overlapX = true; + } + + if (Math.max(y1, r2Y1) < Math.min(y2, r2Y2)) { + overlapY = true; + } + + return overlapX && overlapY; + } +} diff --git a/src/main/java/practiceproblems/jumpGame/JumpGameV.java b/src/main/java/practiceproblems/jumpGame/JumpGameV.java new file mode 100644 index 0000000..0a5f070 --- /dev/null +++ b/src/main/java/practiceproblems/jumpGame/JumpGameV.java @@ -0,0 +1,68 @@ +package practiceproblems.jumpGame; + +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; + +/** + * https://leetcode.com/problems/jump-game-iv/ + */ +public class JumpGameV { + + public int minJumps(int[] arr) { + int n = arr.length; + + if (n == 1) return 0; + + // craeted map holding integer & list + Map> map = new HashMap<>(); + int step = 0; // intial step is 0 + + // Our 1st job is "fill the map" + for (int i = 0; i < n; i++) { + // so, using this function it will check is arr[i] is present or not, if it's not present it would create a new arraylist + // and if it's already present we will add index in it + map.computeIfAbsent(arr[i], v -> new LinkedList<>()).add(i); + } + + // next we need a queue. + Queue q = new LinkedList<>(); + q.offer(0);// in queue we will add our 1st index which is 0 + + while (!q.isEmpty()) { // looping until queue is not empty + step++; // incrementing our step + int size = q.size(); // taking queue size + for (int i = 0; i < size; i++) { // now for each element in this queue for this particulart size running a loop + // so, here we will perform 3 steps + int j = q.poll(); // getting element from queue + + // Jump to j - 1 + if (j - 1 >= 0 && map.containsKey(arr[j - 1])) { + q.offer(j - 1); + } + + // Jump to j + 1 + if (j + 1 < n && map.containsKey(arr[j + 1])) { + // there could be 2 conditions + if (j + 1 == n - 1) return step; // if j+1 is equals to last element + q.offer(j + 1); // otherwise add in queue + } + + // Jump to k --> arr[j] == arr[k] + if (map.containsKey(arr[j])) { // if this particular element hasn't processed + for (int k : map.get(arr[j])) { // so, we will iterate over each k + if (k != j) { // in this we first check if they are not equal, positions are not same + if (k == n - 1) return step; + q.offer(k); + } + } + } + map.remove(arr[j]); // removing from map + } + } + + return step; + } +} diff --git a/src/main/java/practiceproblems/jumpGame/JumpsToReachEnd.java b/src/main/java/practiceproblems/jumpGame/JumpsToReachEnd.java new file mode 100644 index 0000000..1e256d5 --- /dev/null +++ b/src/main/java/practiceproblems/jumpGame/JumpsToReachEnd.java @@ -0,0 +1,124 @@ +package practiceproblems.jumpGame; + +import java.util.ArrayDeque; +import java.util.Arrays; +import java.util.Deque; +import java.util.List; + +public class JumpsToReachEnd { + + /** + * Given an array of non-negative integers nums, + * you are initially positioned at the first index of the array. + * Each element in the array represents your maximum jump length at that position. + *

+ * Determine if you are able to reach the last index + * Input: nums = [2,3,1,1,4] + * Output: true + * Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index. + *

+ * Input: nums = [3,2,1,0,4] + * Output: false + * Explanation: You will always arrive at index 3 no matter what. + * Its maximum jump length is 0, which makes it impossible to reach the last index. + */ + public static boolean canReachEnd(Integer[] nums) { + + int curMax = nums[0]; + for (int i = 1; i < nums.length; i++) { + if (curMax < i) return false; //means we are not able to reach position i + curMax = Math.max(curMax, i + nums[i]); + } + return true; + } + + + /** + * 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 + * Explanation: + * All possible ways to reach at index 3 with value 0 are: + * index 5 -> index 4 -> index 1 -> index 3 + * index 5 -> index 6 -> index 4 -> index 1 -> index 3 + *

+ * Input: arr = [4,2,3,0,3,1,2], start = 0 + * Output: true + * Explanation: + * One possible way to reach at index 3 with value 0 is: + * index 0 -> index 4 -> index 1 -> index 3 + */ + public boolean canReach(int[] arr, int start) { + + if (arr[start] == 0) return true; + + Deque queue = new ArrayDeque<>(); + + queue.offer(new int[]{start, arr[start]}); + boolean[] visited = new boolean[arr.length]; + visited[start] = true; + while (!queue.isEmpty()) { + + int[] temp = queue.poll(); + + int jumpIndexFwd = temp[0] + temp[1]; + int jumpIndexBack = temp[0] - temp[1]; + + if (jumpIndexFwd >= 0 && jumpIndexFwd < arr.length && !visited[jumpIndexFwd]) { + if (arr[jumpIndexFwd] == 0) return true; + visited[jumpIndexFwd] = true; + queue.offer(new int[]{jumpIndexFwd, arr[jumpIndexFwd]}); + } + + if (jumpIndexBack >= 0 && jumpIndexBack < arr.length && !visited[jumpIndexBack]) { + if (arr[jumpIndexBack] == 0) return true; + visited[jumpIndexBack] = true; + queue.offer(new int[]{jumpIndexBack, arr[jumpIndexBack]}); + } + + } + + return false; + } + + + /** + * This is a BFS solution + * + * @param nums + * @return + */ + public int minJump(int[] nums) { + int result = 0, curEnd = 0, curFarthest = 0; + for (int i = 0; i < nums.length - 1; i++) { // loop till last jump hasn't taken us till the end + + //maxReachable is the maximum reachable index from index i. + // Consider this example 2, 3, 10, 1 in this case, maxReachable will be 12 at index 2 which says we can reach till 12th index but not the number of jumps. + // So, if you update it with number of jumps you get 12 where as answer is 2. + + curFarthest = Math.max(curFarthest, i + nums[i]); //Updating the range of next level. + // Similar to queue.push(node) step of BFS but here we are only greedily storing the max reachable index on next level. + + if (i == curEnd) { //When it becomes true, current level iteration has been completed. + result++; + curEnd = curFarthest; //Move on to the next level. + + if (curEnd >= nums.length - 1) { + break; + } + } + } + return result; + } + + public static void main(String[] args) { + + List list = Arrays.asList(3, 3, 1, 0, 2, 0, 1); + System.out.println(canReachEnd(list.toArray(new Integer[list.size()]))); + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/jumpGame/MinTapsToFillGarden.java b/src/main/java/practiceproblems/jumpGame/MinTapsToFillGarden.java new file mode 100644 index 0000000..ce911e2 --- /dev/null +++ b/src/main/java/practiceproblems/jumpGame/MinTapsToFillGarden.java @@ -0,0 +1,38 @@ +package practiceproblems.jumpGame; + +/** + * https://leetcode.com/problems/minimum-number-of-taps-to-open-to-water-a-garden/ + */ +public class MinTapsToFillGarden { + public int minTaps(int n, int[] ranges) { + /** + * i <= end. Suppose ranges = [1,2,1,0,2,1,0,1], arr becomes [3,3,6,0,6,0,8,0]. + * After the 1st step, farCanReach becomes 3, end also becomes 3. + * Now we can choose to move to index 1, index 2, or index 3. + * Since moving to index 2 will give us the largest move (6) in the next step, farCanReach is updated to 6. + * And we can move from there. + */ + for (int i=0; i= n ? steps : -1; + } +} diff --git a/src/main/java/practiceproblems/jumpGame/VideoStitching.java b/src/main/java/practiceproblems/jumpGame/VideoStitching.java new file mode 100644 index 0000000..56a79e6 --- /dev/null +++ b/src/main/java/practiceproblems/jumpGame/VideoStitching.java @@ -0,0 +1,47 @@ +package practiceproblems.jumpGame; + +/** + * https://leetcode.com/problems/video-stitching/ + * + * Observation 1: We will always pick a clip with the maximum coverage if they have same starting points. + * + * eg: [0,3],[0,7],[0,5] -> We pick [0,7] , there is no point of picking others as the problem states that we need to minimize the number of clips picked, + * and this can only be done if we maximize the gap between start and end point of each clip. + * + * Observation 2: Once we start picking the clips from the minimum starting point, + * we only increase the count if we find a starting point greater than previously selected clip's end, + * from then on we keep maximizing the reachable end without increasing count. + */ +public class VideoStitching { + public int videoStitching(int[][] clips, int time) { + + int[] clippings = new int[time + 1]; + + for (int[] clip : clips) { + if (clip[0] > time) continue; + + clippings[clip[0]] = Math.max(clippings[clip[0]], clip[1]); + } + + int currJumpIndex = 0; + int maxJumIndex = 0; + int steps = 0; + + for (int i = 0; i <= time; i++) { + + if (i > maxJumIndex) break; + + maxJumIndex = Math.max(maxJumIndex, clippings[i]); + + if (i >= currJumpIndex) { + + steps++; + + currJumpIndex = maxJumIndex; + if (maxJumIndex >= time) return steps; + } + } + + return maxJumIndex >= time ? steps : -1; + } +} diff --git a/src/main/java/practiceproblems/mergesort/CountNumbersLessThanSelf.java b/src/main/java/practiceproblems/mergesort/CountNumbersLessThanSelf.java new file mode 100644 index 0000000..36af8a6 --- /dev/null +++ b/src/main/java/practiceproblems/mergesort/CountNumbersLessThanSelf.java @@ -0,0 +1,104 @@ +package practiceproblems.mergesort; + +import java.util.LinkedList; +import java.util.List; + +/** + * You are given an integer array nums and you have to return a new counts array. + * The counts array has the property where counts[i] is the number of smaller elements to the right of nums[i]. + * Input: [5,2,6,1] + * Output: [2,1,1,0] + */ +class CountNumbersLessThanSelf { + // Wrapper class for each and every value of the input array, +// to store the original index position of each value, before we merge sort the array + private static class ArrayValWithOrigIdx { + int val; + int idx; + + public ArrayValWithOrigIdx(int val, int idx) { + this.val = val; + this.idx = idx; + } + } + + public List countSmaller(int[] nums) { + if (nums == null || nums.length == 0) return new LinkedList<>(); + int n = nums.length; + int[] result = new int[n]; + + ArrayValWithOrigIdx[] newNums = new ArrayValWithOrigIdx[n]; + + for (int i = 0; i < n; ++i) newNums[i] = new ArrayValWithOrigIdx(nums[i], i); + + mergeSortAndCount(newNums, 0, n - 1, result); + + // notice we don't care about the sorted array after merge sort finishes. + // we only wanted the result counts, generated by running merge sort + List resultList = new LinkedList(); + for (int i : result) resultList.add(i); + return resultList; + } + + private void mergeSortAndCount(ArrayValWithOrigIdx[] nums, int start, int end, int[] result) { + if (start >= end) return; + + int mid = (start + end) / 2; + mergeSortAndCount(nums, start, mid, result); + mergeSortAndCount(nums, mid + 1, end, result); + + // left subarray start...mid + // right subarray mid+1...end + int leftPos = start; + int rightPos = mid + 1; + LinkedList merged = new LinkedList<>(); + int numElemsRightArrayLessThanLeftArray = 0; + while (leftPos <= mid && rightPos <= end) { + if (nums[leftPos].val > nums[rightPos].val) { + // this code block is exactly what the problem is asking us for: + // a number from the right side of the original input array, is smaller + // than a number from the left side + // + // within this code block, + // nums[rightPos] is smaller than the start of the left sub-array. + // Since left sub-array is already sorted, + // nums[rightPos] must also be smaller than the entire remaining left sub-array + ++numElemsRightArrayLessThanLeftArray; + + // continue with normal merge sort, merge + merged.add(nums[rightPos]); + ++rightPos; + } else { + // a number from left side of array, is smaller than a number from + // right side of array + result[nums[leftPos].idx] += numElemsRightArrayLessThanLeftArray; + + // Continue with normal merge sort + merged.add(nums[leftPos]); + ++leftPos; + } + } + + // part of normal merge sort, if either left or right sub-array is not empty, + // move all remaining elements into merged result + while (leftPos <= mid) { + result[nums[leftPos].idx] += numElemsRightArrayLessThanLeftArray; + + merged.add(nums[leftPos]); + ++leftPos; + } + while (rightPos <= end) { + merged.add(nums[rightPos]); + ++rightPos; + } + + // part of normal merge sort + // copy back merged result into array + int pos = start; + for (ArrayValWithOrigIdx m : merged) { + nums[pos] = m; + ++pos; + } + } +} + diff --git a/src/main/java/practiceproblems/mergesort/CountOfRanges.java b/src/main/java/practiceproblems/mergesort/CountOfRanges.java new file mode 100644 index 0000000..f076003 --- /dev/null +++ b/src/main/java/practiceproblems/mergesort/CountOfRanges.java @@ -0,0 +1,61 @@ +package practiceproblems.mergesort; + +/** + * https://leetcode.com/problems/count-of-range-sum/ + */ +public class CountOfRanges { + + int count = 0; + int lower; + int upper; + + public int countRangeSum(int[] nums, int lower, int upper) { + long[] sum = new long[nums.length + 1]; + long[] temp = new long[nums.length + 1]; + sum[0] = 0; + this.lower = lower; + this.upper = upper; + for (int i = 1; i <= nums.length; i++) { + sum[i] = sum[i - 1] + nums[i - 1]; + } + + mergesort(sum, 0, sum.length - 1, temp); + return count; + } + + private void mergesort(long[] sum, int start, int end, long[] temp) { + if (start >= end) { + return; + } + int mid = start + (end - start) / 2; + mergesort(sum, start, mid, temp); + mergesort(sum, mid + 1, end, temp); + merge(sum, start, mid, end, temp); + } + + private void merge(long[] sum, int start, int mid, int end, long[] temp) { + int right = mid + 1; + int index = start; + int low = mid + 1, high = mid + 1; + for (int left = start; left <= mid; left++) { + while (low <= end && sum[low] - sum[left] < lower) { + low++; + } + while (high <= end && sum[high] - sum[left] <= upper) { + high++; + } + while (right <= end && sum[right] < sum[left]) { + temp[index++] = sum[right++]; + } + temp[index++] = sum[left]; + count += high - low; + } + while (right <= end) { + temp[index++] = sum[right++]; + } + + for (int i = start; i <= end; i++) { + sum[i] = temp[i]; + } + } +} diff --git a/src/main/java/practiceproblems/mergesort/CountingInversion.java b/src/main/java/practiceproblems/mergesort/CountingInversion.java new file mode 100644 index 0000000..bc9b075 --- /dev/null +++ b/src/main/java/practiceproblems/mergesort/CountingInversion.java @@ -0,0 +1,62 @@ +package practiceproblems.mergesort; + +/** + * https://www.geeksforgeeks.org/counting-inversions/ + */ +class CountingInversion { + + static int mergeSort(int[] arr, int arrSize) { + return mergeSort(arr, 0, arrSize - 1); + } + + static int mergeSort(int[] arr, int left, int right) { + if (left >= right) + return 0; + int invCount = 0; + int mid = ((right - left) / 2) + left; + + invCount += mergeSort(arr, left, mid); + invCount += mergeSort(arr, mid + 1, right); + + invCount += merge(arr, left, mid + 1, right); + + return invCount; + } + + static int merge(int[] arr, int left, int mid, int right) { + int invCount = 0; + int[] temp = new int[arr.length]; + int i = left; + int j = mid+1; + int k = left; + while ((i <= mid) && (j <= right)) { + if (arr[i] <= arr[j]) { + temp[k++] = arr[i++]; + } else { + temp[k++] = arr[j++]; + // the reason to put mid-i is take example of [1,3,5] [2,4,6] + // left is 0 and right is mid at start + // when i=1 and j=0 (value 3 and 2) we see an inversion, since + // the first part is sorted and values after i=1(3) will be greater than j=0(2) + // so we consider all elements after 3 as inversions + invCount += mid - i + 1; + } + } + + while (i <= mid - 1) + temp[k++] = arr[i++]; + while (j <= right) + temp[k++] = arr[j++]; + + for (i = left; i <= right; i++) + arr[i] = temp[i]; + + return invCount; + } + + public static void main(String[] args) { + //int arr[] = new int[] { 4, 6, 2, 1, 9, 7 }; + int arr[] = new int[]{5, 1, 4, 2}; + System.out.println("Number of inversions are " + mergeSort(arr, arr.length)); + } +} diff --git a/src/main/java/practiceproblems/mergesort/MergeSort.java b/src/main/java/practiceproblems/mergesort/MergeSort.java new file mode 100644 index 0000000..570bed4 --- /dev/null +++ b/src/main/java/practiceproblems/mergesort/MergeSort.java @@ -0,0 +1,47 @@ +package practiceproblems.mergesort; + +import java.util.Arrays; + +class MergeSort { + + static void mergeSort(int[] arr, int left, int right) { + if (left < right) { + int mid = ((right - left) / 2) + left; + + mergeSort(arr, left, mid); + mergeSort(arr, mid + 1, right); + + merge(arr, left, mid, right); + } + } + + static void merge(int[] arr, int left, int mid, int right) { + int[] temp = new int[arr.length]; + int i = left; + int j = mid + 1; + int k = left; + while ((i <= mid) && (j <= right)) { + if (arr[i] < arr[j]) { + temp[k++] = arr[i++]; + } else { + temp[k++] = arr[j++]; + } + } + + while (i <= mid) + temp[k++] = arr[i++]; + while (j <= right) + temp[k++] = arr[j++]; + + for (i = left; i <= right; i++) + arr[i] = temp[i]; + + } + + public static void main(String[] args) { + int []arr = new int[] { 8, 4, 1,6,6,9,2, 2 }; + + mergeSort(arr, 0,arr.length - 1); + System.out.println(Arrays.toString(arr)); + } +} diff --git a/src/main/java/practiceproblems/mergesort/ReversePairs.java b/src/main/java/practiceproblems/mergesort/ReversePairs.java new file mode 100644 index 0000000..33f7798 --- /dev/null +++ b/src/main/java/practiceproblems/mergesort/ReversePairs.java @@ -0,0 +1,58 @@ +package practiceproblems.mergesort; + +public class ReversePairs { + + public int reversePairs(int[] nums) { + return mergeSort(nums, 0, nums.length - 1); + + } + + int mergeSort(int[] arr, int left, int right) { + if (left >= right) { + return 0; + } + int invCount = 0; + int mid = ((right + left) / 2); + + invCount += mergeSort(arr, left, mid); + invCount += mergeSort(arr, mid + 1, right); + // Count reverse pairs across the two sorted halves + int i = left, j = mid + 1; + while (i <= mid && j <= right) { + if ((long) arr[i] > 2 * (long) arr[j]) { + invCount += (mid - i + 1); // All elements from i to mid are greater than 2*nums[j] + j++; + } else { + i++; + } + } + merge(arr, left, mid, right); + return invCount; + } + + void merge(int[] arr, int left, int mid, int right) { + int invCount = 0; + int rightArrStart = mid + 1; + int[] temp = new int[arr.length]; + + int i = left; + int j = mid + 1; + int k = left; + while ((i <= mid) && (j <= right)) { + if (arr[i] <= arr[j]) { + temp[k++] = arr[i++]; + } else { + temp[k++] = arr[j++]; + } + } + + while (i <= mid) + temp[k++] = arr[i++]; + while (j <= right) + temp[k++] = arr[j++]; + + for (i = left; i <= right; i++) + arr[i] = temp[i]; + } + +} diff --git a/src/main/java/practiceproblems/prefixsum/IntervalsBetweenIdenticalElements.java b/src/main/java/practiceproblems/prefixsum/IntervalsBetweenIdenticalElements.java new file mode 100644 index 0000000..a22b325 --- /dev/null +++ b/src/main/java/practiceproblems/prefixsum/IntervalsBetweenIdenticalElements.java @@ -0,0 +1,54 @@ +package practiceproblems.prefixsum; + +import java.util.HashMap; +import java.util.Map; + +public class IntervalsBetweenIdenticalElements { + + /** + * https://leetcode.com/problems/intervals-between-identical-elements + * + * Consider 1's at different postions of an array. + * x y z p q + * 1 1 1 1 1 + * + * consider 1 at index z: |z - x| + |z - y| + |z - p| + |z - q| + * + * when we are looping from left to right we are storing sum and count of previous indices of num in maps. + * |z - x| + |z - y| = z - x + z - y, since z is greater than x and y. + * z - x + z - y = 2z - (x + y) = (count) * (currentIndex) - (sum). + * + * Similarly, we can calculate the |z - p| + |z - q| when we loop from right to left. + * + * @param arr + * @return + */ + public long[] getDistances(int[] arr) { + long[] output = new long[arr.length]; + Map sumMap = new HashMap<>(); + Map countMap = new HashMap<>(); + for (int i = 0; i < arr.length; ++i) { + long leftsum = sumMap.getOrDefault(arr[i], 0L); + int leftcount = countMap.getOrDefault(arr[i], 0); + + output[i] += i * (long) leftcount - leftsum; + sumMap.put(arr[i], leftsum + i); + countMap.put(arr[i], leftcount + 1); + } + + sumMap = new HashMap<>(); + countMap = new HashMap<>(); + int len = arr.length; + for (int i = len - 1; i >= 0; --i) { + //since now the current index goes from right to left, all of the prev would be greater than our current idx + long rightsum = sumMap.getOrDefault(arr[i], 0L); + int rightcount = countMap.getOrDefault(arr[i], 0); + + output[i] += rightsum - i * (long) rightcount; + sumMap.put(arr[i], rightsum + i); + countMap.put(arr[i], rightcount + 1); + } + + return output; + } +} diff --git a/src/main/java/practiceproblems/prefixsum/MaxNegativeNumbers.java b/src/main/java/practiceproblems/prefixsum/MaxNegativeNumbers.java new file mode 100644 index 0000000..f83bf29 --- /dev/null +++ b/src/main/java/practiceproblems/prefixsum/MaxNegativeNumbers.java @@ -0,0 +1,42 @@ +package practiceproblems.prefixsum; + +import java.util.Collections; +import java.util.PriorityQueue; + +public class MaxNegativeNumbers { + + + /** + * We add each number to the priority queue and increment ans, assuming we can keep it negative. + * If the sum becomes non-positive, we need to make some numbers positive to keep the overall sum positive. + * We choose the largest available number (top of the priority queue) to make positive, as this allows us to keep more numbers negative overall. + * When we make a number positive, we add twice its value to the sum (reversing the initial subtraction and then adding it). + * We decrease ans each time we make a number positive. + * @param numbers + * @return + */ + public static int maxNegativeSigns(int[] numbers) { + int sum = 0; + PriorityQueue pq = new PriorityQueue<>(Collections.reverseOrder()); + int ans = 0; + + for (int val : numbers) { + sum -= val; + pq.offer(val); + ans++; + while (!pq.isEmpty() && sum <= 0) { + int top = pq.peek(); + sum += 2 * top; // make it positive by adding twice its value + pq.poll(); + ans--; + } + } + return ans; + + } + + public static void main(String[] args) { + System.out.println(maxNegativeSigns(new int[]{3, 2, 1, 1, 1,1})); + System.out.println(maxNegativeSigns(new int[]{1, 2, 3, 4, 5})); + } +} diff --git a/src/main/java/practiceproblems/prefixsum/SubArrayDivisibleByP.java b/src/main/java/practiceproblems/prefixsum/SubArrayDivisibleByP.java new file mode 100644 index 0000000..fefdc2f --- /dev/null +++ b/src/main/java/practiceproblems/prefixsum/SubArrayDivisibleByP.java @@ -0,0 +1,52 @@ +package practiceproblems.prefixsum; + +import java.util.HashMap; + +/** + * https://leetcode.com/problems/make-sum-divisible-by-p/ + *

+ * Calculate the rem = sum(nums) % p, which means we need to remove a subarray which has sum % p == rem to make the rest sum divisible by p. + * It also requires that the removed subarray should be the shortest subarray. + * Then the questions become: Find the shortest array with sum(subarray) % p == rem. + * Since we need the shortest length, last[remainder] = idx records the last index that (A[0] + A[1] + ... + A[i]) % p == remainder. + * Everytime, we find a possible sum, we need get the right-most index to make sure that the subarray removed is the shortest. + * If there is no element what we want, then i - (-N) > N. Since we cannot remove the whole array, therefore, return res when res < N. + */ +public class SubArrayDivisibleByP { + + public static int minSubarray(int[] nums, int p) { + + int remainder = 0, prefixSum = 0; + for (int num : nums) { + remainder = (remainder + num) % p; + } + + if (remainder == 0) return 0; + + + int result = nums.length; + var prefixSumToIndex = new HashMap(); + prefixSumToIndex.put(0, -1); + + for (int i = 0; i < nums.length; i++) { + prefixSum = (prefixSum + nums[i]) % p; + + prefixSumToIndex.put(prefixSum, i); + + int key = (prefixSum - remainder + p) % p; //Avoid the case where prefixSum - remainder is negative + + if (prefixSumToIndex.containsKey(key)) { + result = Math.min(result, i - prefixSumToIndex.get(key)); + } + + + } + + return result == nums.length ? -1 : result; + + } + + public static void main(String[] args) { + minSubarray(new int[]{3,1,4,2},6); + } +} diff --git a/src/main/java/practiceproblems/prefixsum/SubArraySumDivisibleByK.java b/src/main/java/practiceproblems/prefixsum/SubArraySumDivisibleByK.java new file mode 100644 index 0000000..f8ce79c --- /dev/null +++ b/src/main/java/practiceproblems/prefixsum/SubArraySumDivisibleByK.java @@ -0,0 +1,55 @@ +package practiceproblems.prefixsum; + +import java.util.HashMap; +import java.util.Map; + +/** + * let's take an array with random values and K=5 + * Si = sum till 0,.. ith pos + * Sj = sum till 0,..jth pos + * + * [x,x,x,x,x,x,x,x,x,x,x,x,x] + * Si Sj + * 22 52 + * at Si the remainder/surplus is 2 + * at Sj the remainder/surplus is still 2 + * that means in between the subarry i...j the sum is divisible by K + */ +public class SubArraySumDivisibleByK { + + public int subarraysDivByK(int[] A, int K) { + Map count = new HashMap<>(); + count.put(0, 1); //if the first prefix sum is 0, it need to be counted as a mod of K, right? + // Rest of prefix sum from 1 to K-1 don't include the first prefix sum + int prefix = 0, res = 0; + for (int a : A) { + //(prefix+a%K+K)%K is just a trick to make the remainder positive. + prefix = (prefix + a % K + K) % K; + res += count.getOrDefault(prefix, 0); + count.put(prefix, count.getOrDefault(prefix, 0) + 1); + } + return res; + } + + // A = [4,5,0,-2,-3,1], K = 5 + // step 1 : {0:1} a=4 sum=4 mod=4 count = 0+0 =0 + // step 2 : {0:1,4:1} a=5 sum=9 mod=4 count = 0+1 =1 + // step 3 : {0:1,4:2} a=0 sum=9 mod=4 count = 1+2 =3 + // step 4 : {0:1,4:3} a=-2 sum=7 mod=2 count = 3+0 =3 + // step 6 : {0:1,4:3,2:1} a=-3 sum=4 mod=4 count = 3+3 =6 + // step 7 : {0:1,4:4,2:1} a=1 sum=5 mod=0 count = 6+1 =7 + public int subarraysDivByKOptimised(int[] A, int K) { + int[] map = new int[K]; // this optimisation is when we do % we can have result at most K + + map[0] = 1; + int prefix = 0, res = 0; + for (int a : A) { + prefix = (prefix + a % K + K) % K; + // trick is to add the map[prefix] to result before incrementing it + + res += map[prefix]; + map[prefix]++; + } + return res; + } +} diff --git a/src/main/java/practiceproblems/prefixsum/SubArraySumEqualsK.java b/src/main/java/practiceproblems/prefixsum/SubArraySumEqualsK.java new file mode 100644 index 0000000..481e4c5 --- /dev/null +++ b/src/main/java/practiceproblems/prefixsum/SubArraySumEqualsK.java @@ -0,0 +1,46 @@ +package practiceproblems.prefixsum; + +import java.util.HashMap; +import java.util.Map; + +/** + * https://leetcode.com/problems/subarray-sum-equals-k/ + */ +public class SubArraySumEqualsK { + public int subarraySum(int[] nums, int k) { + int left = 0; + + int sum = 0; + int result = 0; + + Map map = new HashMap<>(); + map.put(0, 1); + // Let’s assume (D+E+3=k) + // sum =A+B+C+D+E+3 + // preSum = A+B+C + // Thus, we can compose critical equation + // sum - preSum = k + // Since we’re looking for specific preSum to compose value k + // we can re-arrange the above equation like below + // sum - k = preSum + // For multiple matching counts + // Ex: + // input [0,-1,1,2,3] k=5 + // We notice that it’s necessary to record occurrence of specific preSum. + // In this case, we need to know preSum 2 occur three times. [0,-1,1,2], [-1,1,2], [2] + // This indicate that it's necessary to record preSum counts + + while (left < nums.length) { + sum += nums[left]; + if (map.get(sum - k) != null) { + result += map.get(sum - k); + } + map.put(sum, map.getOrDefault(sum, 0) + 1); + left++; + + } + return result; + + } + +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/prefixsum/SubstringEqualFragments.java b/src/main/java/practiceproblems/prefixsum/SubstringEqualFragments.java new file mode 100644 index 0000000..9ca9398 --- /dev/null +++ b/src/main/java/practiceproblems/prefixsum/SubstringEqualFragments.java @@ -0,0 +1,118 @@ +package practiceproblems.prefixsum; + + +import java.util.HashMap; +import java.util.Map; + +/** + * https://www.youtube.com/watch?v=9SS83X6Na6k + *

+ * Microsoft interview + *

+ * There are two strings, A and B, each of length N. + * A fragment of string A Corresponds with a fragment of string B if: both fragments start at the same position; + * letters from one fragment can be rearranged into the order of letters in the other fragment + * (note that the case and number of occurrences of the letter matters). + *

+ * Given A "dBacaAA" and B = "caBdaaA", the function should return 5. The corresponding fragments are: + * "dBaca" and "caBda" (starting at position 0) + * "dBac" and "caBd"(starting at position 0) + * ."Ba" and "aB" (starting at position 1) + * ."a" and "a" (starting at position 4) + * . "A" and "A" (at position 6). + *

+ * Given A = "zzzX" and B "zzzX",the function should return 10. total substrings = (n*(n+1))/2 + * All fragments starting at the same positions in both strings correspond. + */ +public class SubstringEqualFragments { + + public static int countFragmentsOptimised(String A, String B) { + int count = 0; + int strLength = A.length(); + Map letters = new HashMap<>(); + + + int noOfCharacterToBeChecked = 1; + int startIndex = 0; + while (noOfCharacterToBeChecked <= strLength) { + + int lastIndex = startIndex + noOfCharacterToBeChecked; + //First get the characters in HashMap for the noOfLtters to be checked + for (int i = startIndex; i < lastIndex; i++) { + letters.put(A.charAt(i), letters.getOrDefault(A.charAt(i), 0) + 1); + } + //compare the second string and increase count if matched + for (int i = startIndex; i < lastIndex; i++) { + if (letters.containsKey(B.charAt(i))) { + int letterCount = letters.getOrDefault(B.charAt(i), 0); + if (letterCount > 1) { + letters.put(B.charAt(i), letters.getOrDefault(B.charAt(i), 0) - 1); + } else { + letters.remove(B.charAt(i)); + } + } else { + break; + } + } + + if (letters.isEmpty()) { + count++; + } + letters.clear(); + //Check till last index + if (lastIndex < strLength) { + startIndex++; + } else { + //Once last index is reached increase noOfLetter to be checked and reset startIndex + noOfCharacterToBeChecked++; + startIndex = 0; + } + + } + return count; + } + + + public static int countFragmentsBruteForce(String a, String b) { + int[] cache1; + int[] cache2; + int ans = 0; + for (int i = 0; i < a.length(); i++) { + cache1 = new int[52]; + cache2 = new int[52]; + int totalCountOfParity = 52; // at first all the values in cache has same value i.e 0 + for (int j = i; j < a.length(); j++) { + int c1 = mapToInt(a.charAt(j)); + int c2 = mapToInt(b.charAt(j)); + + if (cache1[c1] == cache2[c1]) totalCountOfParity--; + + cache1[c1]++; + + if (cache1[c1] == cache2[c1]) totalCountOfParity++; + + if (cache1[c2] == cache2[c2]) totalCountOfParity--; + + cache2[c2]++; + + if (cache1[c2] == cache2[c2]) totalCountOfParity++; + + if (totalCountOfParity == 52) ans++; + } + } + return ans; + } + + public static int mapToInt(char ch) { + if (ch >= 'a' && ch <= 'z') return (ch - 'a'); + if (ch >= 'A' && ch <= 'Z') return (ch - 'A' + 26); + return -1; + } + + public static void main(String[] args) { + String s1 = "dBacaAA"; + String s2 = "caBdaaA"; + System.out.println(countFragmentsOptimised(s1, s2)); + } + +} diff --git a/src/main/java/practiceproblems/prefixsum/UniqueSubArrayDivisibleByK.java b/src/main/java/practiceproblems/prefixsum/UniqueSubArrayDivisibleByK.java new file mode 100644 index 0000000..6f1a93a --- /dev/null +++ b/src/main/java/practiceproblems/prefixsum/UniqueSubArrayDivisibleByK.java @@ -0,0 +1,61 @@ +package practiceproblems.prefixsum; + +import java.util.HashSet; +import java.util.Set; + +/** + * https://leetcode.com/problems/k-divisible-elements-subarrays/ + * + * It's tempting to apply a sliding window technique here, however, it won't help us identify distinct subarrays. + * Because constraints are low (n <= 200), we can just identify all valid subarrays O(n ^ 2), + * and use a set to dedup them O(n). So, the overall complexity would be O(n ^ 3). + * + * For O(n ^ 2) solution, we can use a rolling hash (Rabin-Karp). + * Note that we only need to check hashes for arrays of the same size, which reduces the collision probability. + * + * Rolling Hash (Simple) + * We need to do the collision check, but here I omitted it for simplicity (see third solution below if you want to handle collisions). + */ +public class UniqueSubArrayDivisibleByK { + + public int countDistinct(int[] nums, int k, int p) { + int n = nums.length; + // we are storing hashcode for all the substrings so that we can compare them faster. + // main goal is to avoid entire sub array comparision using hashcode. + Set ways = new HashSet<>(); + for(int i = 0; i < n; i++) { + int cnt = 0; + long hc = 1; // this is the running hashcode for sub array [i...j] + for(int j = i; j < n; j++) { + hc = 199L * hc + nums[j]; // updating running hashcode, since we nums are <=200, we shall consider a prime near 200 to avoid collision + if(nums[j] % p == 0) + cnt++; + if(cnt <= k) { // if current subarray [i...j] is valid, add its hashcode in our storage. + ways.add(hc); + } + } + } + return ways.size(); + } + // for (int i = 0; i < n; i++) { + // StringBuilder sb = new StringBuilder(); + // int t = 0; + // for (int j = i; j < n ; j++) { + // sb.append(nums[j] + ","); + // if(nums[j] % p == 0){ + // t++; + // } + // if(t > k){ + // break; + // } + // + // set.add(sb.toString()); + // } + // } + + public static void main(String[] args) { + UniqueSubArrayDivisibleByK uniqueSubArrayDivisibleByK = new UniqueSubArrayDivisibleByK(); + System.out.println(uniqueSubArrayDivisibleByK.countDistinct(new int[]{2,3,3,2,2}, 2, 2)); + System.out.println(uniqueSubArrayDivisibleByK.countDistinct(new int[]{1, 2, 3, 4}, 4, 1)); + } +} diff --git a/src/main/java/practiceproblems/recursion/NQueens.java b/src/main/java/practiceproblems/recursion/NQueens.java new file mode 100644 index 0000000..0a2bb95 --- /dev/null +++ b/src/main/java/practiceproblems/recursion/NQueens.java @@ -0,0 +1,85 @@ +package practiceproblems.recursion; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +/** + * https://leetcode.com/problems/n-queens/ + *

+ * This is a perfect problem for backtracking - place the queens one by one, and when all possibilities are exhausted, + * backtrack by removing a queen and placing it elsewhere. + */ +public class NQueens { + + public List> solveNQueens(int n) { + if (n == 0) return Collections.emptyList(); + + List> result = new ArrayList<>(); + char[][] board = new char[n][n]; + for (int i = 0; i < n; i++) { + Arrays.fill(board[i], '.'); + } + + nQueensHelper(0, n, board, result); + + return result; + } + + public void nQueensHelper(int row, int n, char[][] board, List> result) { + + if (row == board.length) { + result.add(construct(board)); + return; + } + for (int i = 0; i < n; i++) { + // check if it can be placed in current i(col) of the row and explore possibilities + if (isValidPlacement(row, i, board)) { + board[row][i] = 'Q'; // if yes proceed to next row and explore all possibilities + nQueensHelper(row + 1, n, board, result); + board[row][i] = '.'; + } + + } + + } + + public boolean isValidPlacement(int row, int col, char[][] chess) { + // check all cols + for (int i = 0; i < row; i++) { + if (chess[i][col] == 'Q') { + return false; + } + } + int x = row; + int y = col; + //checking for upper left diagonal(non-main diagonal), row is decreasing and col too + while (x >= 0 && y >= 0) { + if (chess[x][y] == 'Q') { + return false; + } + --x; + --y; + } + + //checking for main diagonal(upper right), row is decreasing and col increasing + while (row >= 0 && col < chess.length) { + if (chess[row][col] == 'Q') { + return false; + } + --row; + ++col; + } + return true; + } + + private List construct(char[][] chess) { + List path = new ArrayList<>(); + for (char[] chars : chess) { + path.add(new String(chars)); + } + return path; + } + +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/recursion/SudokuSolver.java b/src/main/java/practiceproblems/recursion/SudokuSolver.java new file mode 100644 index 0000000..61f6568 --- /dev/null +++ b/src/main/java/practiceproblems/recursion/SudokuSolver.java @@ -0,0 +1,87 @@ +package practiceproblems.recursion; + +import java.util.Arrays; + +/** + * tricky recursion + */ +public class SudokuSolver { + public void solveSudoku(char[][] board) { + solveBoard(board, 0, 0); + } + + private boolean solveBoard(char[][] board, int currentRow, int currentCol) { + for (int row = currentRow; row < 9; row++, currentCol = 0) { + for (int col = currentCol; col < 9; col++) { + if (board[row][col] == '.') { + for (char num = '1'; num <= '9'; num++) { + if (isValid(board, row, col, num)) { + board[row][col] = num; + if (solveBoard(board, row, col + 1)) { + return true; + } + board[row][col] = '.'; + } + } + + return false; + } + } + } + + return true; + } + + // Make sure the digit doesn't exist in the current row, col or square. + private boolean isValid(char[][] board, int row, int col, char num) { + int regionRow = (row / 3) * 3; //region start row + int regionCol = (col / 3) * 3; //region start col + for (int i = 0; i < 9; i++) { + // [regionRow + i / 3][regionCol + i % 3] check 3*3 block + if (board[i][col] == num || board[row][i] == num || board[regionRow + i / 3][regionCol + i % 3] == num) { + return false; + } + } + + return true; + } + + + /** + * start from 0 and go till 81 (9*9), for each cell do a dfs, if not backtrack and change the value + * + * @param board + */ + public void solveSudokuDFS(char[][] board) { + dfs(board, 0); + } + + private boolean dfs(char[][] board, int d) { + if (d == 81) return true; //found solution + int i = d / 9, j = d % 9; + if (board[i][j] != '.') return dfs(board, d + 1);//prefill number skip + + boolean[] flag = new boolean[10]; + validate(board, i, j, flag); + for (int k = 1; k <= 9; k++) { + if (flag[k]) { + board[i][j] = (char) ('0' + k); + if (dfs(board, d + 1)) return true; + } + } + board[i][j] = '.'; //if you can not solve, in the wrong path, change back to '.' and out + return false; + } + + // in arr[0..9] fill the values with numbers in row and col as false + private void validate(char[][] board, int i, int j, boolean[] flag) { + Arrays.fill(flag, true); + for (int k = 0; k < 9; k++) { + if (board[i][k] != '.') flag[board[i][k] - '0'] = false; + if (board[k][j] != '.') flag[board[k][j] - '0'] = false; + int r = i / 3 * 3 + k / 3; + int c = j / 3 * 3 + k % 3; + if (board[r][c] != '.') flag[board[r][c] - '0'] = false; + } + } +} diff --git a/src/main/java/practiceproblems/stack/AsteroidCollision.java b/src/main/java/practiceproblems/stack/AsteroidCollision.java new file mode 100644 index 0000000..3e473a8 --- /dev/null +++ b/src/main/java/practiceproblems/stack/AsteroidCollision.java @@ -0,0 +1,35 @@ +package practiceproblems.stack; + +import java.util.ArrayDeque; +import java.util.Deque; + +/** + * https://leetcode.com/problems/asteroid-collision + */ +public class AsteroidCollision { + + public int[] asteroidCollision(int[] asteroids) { + if (asteroids.length <= 1) return asteroids; + Deque stack = new ArrayDeque<>(); + for (int asteroid : asteroids) { + if (asteroid > 0) { // Pushing all +ve asteroid + stack.push(asteroid); + } else { + // Remove all positive asteroid before our current asteroid + while (!stack.isEmpty() && stack.peek() > 0 && Math.abs(stack.peek()) < Math.abs(asteroid)) + stack.pop(); + // Checking if the stack is empty or the recent asteroid is negative! + if (stack.isEmpty() || stack.peek() < 0) + stack.push(asteroid); + // If recent asteroid <= our asteroid, We broke our outer loop if equal we pop it. + else if (stack.peek() == Math.abs(asteroid)) + stack.pop(); + } + } + int[] output = new int[stack.size()]; + for (int i = output.length - 1; i >= 0; i--) + output[i] = stack.pop(); + + return output; + } +} diff --git a/src/main/java/practiceproblems/stack/AtomCounts.java b/src/main/java/practiceproblems/stack/AtomCounts.java new file mode 100644 index 0000000..284cf2c --- /dev/null +++ b/src/main/java/practiceproblems/stack/AtomCounts.java @@ -0,0 +1,59 @@ +package practiceproblems.stack; + +import java.util.*; + +// https://leetcode.com/problems/number-of-atoms/ +public class AtomCounts { + public String countOfAtoms(String formula) { + + var stack = new Stack>(); + stack.push(new HashMap<>()); + int len = formula.length(); + for (int i = 0; i < formula.length();) { + + if (formula.charAt(i) == '(') { + stack.push(new HashMap<>()); + i++; + continue; + } + if (formula.charAt(i) == ')') { + Map top = stack.pop(); + i++; + int number = 0; + int start = i; + while (i < len && Character.isDigit(formula.charAt(i))){ + i++; + } + int multiplier = start < i ? Integer.parseInt(formula.substring(start, i)) : 1; + for (String key : top.keySet()) { + stack.peek().put(key, stack.peek().getOrDefault(key, 0) + top.get(key) * multiplier); + } + continue; + } + int start = i; + i++; + while (i < len && Character.isLowerCase(formula.charAt(i))) + i++; + String element = formula.substring(start, i); + start = i; + while (i < len && Character.isDigit(formula.charAt(i))) { + i++; + } + int count = start < i ? Integer.parseInt(formula.substring(start, i)) : 1; + stack.peek().put(element, stack.peek().getOrDefault(element, 0) + count); + } + + Map result = stack.pop(); + List elements = new ArrayList<>(result.keySet()); + Collections.sort(elements); + StringBuilder sb = new StringBuilder(); + for (String element : elements) { + sb.append(element); + int count = result.get(element); + if (count > 1) + sb.append(count); + } + return sb.toString(); + } + +} diff --git a/src/main/java/practiceproblems/stack/BasicCalculator.java b/src/main/java/practiceproblems/stack/BasicCalculator.java new file mode 100644 index 0000000..346cef3 --- /dev/null +++ b/src/main/java/practiceproblems/stack/BasicCalculator.java @@ -0,0 +1,96 @@ +package practiceproblems.stack; + +import java.util.ArrayDeque; +import java.util.Deque; + +// Input: "(1+(4+5+2)-3)+(6+8)" +// Output: 23 +public class BasicCalculator { + + public static void main(String[] args) { + System.out.println(calculate("-26-(5-6)")); + System.out.println(calculate("(1+(4+5+2)-3)+(6+8)")); + } + + public static int calculate(String s) { + if (s == null || s.isEmpty()) return -1; + Deque deque = new ArrayDeque<>(); + int sign = 1; + int number = 0; + int result = 0; + // let's take an edge case 2-(5-6)=3 + // at i=0 number=2 + // i=1 char ='-' update with prev seen sign res=sign*number reset number we are looking for next operand + //i=2 char='(' and sign is '-', push prev result and sign and reset result for calculating + // sub problem inside braces + // i=3 update number to 5 + // i=4 char ='-' update result as sign*number = 5 reset number and sign =-1 + //i=5 update number to 6 + // i=6 char=')' update result with existing number(res=5=> 5+(-1*6)) and sign inside the braces + // then pop, which is last seen sign outside brace=> -1 and pop again to get result outside brace + // add all to result + + for (int i = 0; i < s.length(); i++) { + char temp = s.charAt(i); + if (Character.isDigit(temp)) { + number = number * 10 + temp - '0'; + } else if (temp == '+') { + result += sign * number; + number = 0; + sign = 1; + } else if (temp == '-') { + result += sign * number; + number = 0; + sign = -1; + } else if (temp == '(') { + deque.addLast(result); + deque.addLast(sign); + + result = 0; + sign = 1; + } else if (temp == ')') { + result += sign * number; + number = 0; + result *= deque.removeLast(); + result += deque.removeLast(); + + } + } + if (number != 0) result += sign * number; + return result; + } + + /** + * Example 1: + * Input: s = "(abcd)" + * Output: "dcba" + * Example 2: + * Input: s = "(u(love)i)" + * Output: "iloveu" + * Explanation: The substring "love" is reversed first, then the whole string is reversed. + */ + public String reverseParentheses(String s) { + + StringBuilder sb = new StringBuilder(); + Deque queue = new ArrayDeque<>(); + + for(char ch:s.toCharArray()){ + + if (ch=='('){ + queue.push(sb.length()); + }else if (ch==')'){ + var reversed = new StringBuilder(); + for(int j= sb.length()-queue.pop();j>0;--j){ + reversed.append(sb.charAt(sb.length()-1)); + sb.deleteCharAt(sb.length()-1); + } + sb.append(reversed); + }else{ + sb.append(ch); + } + } + + return sb.toString(); + } + +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/stack/BasicCalculatorII.java b/src/main/java/practiceproblems/stack/BasicCalculatorII.java new file mode 100644 index 0000000..df06e63 --- /dev/null +++ b/src/main/java/practiceproblems/stack/BasicCalculatorII.java @@ -0,0 +1,49 @@ +package practiceproblems.stack; + +import java.util.ArrayDeque; +import java.util.Deque; + +/** + * tricky sign maintenance + */ +public class BasicCalculatorII { + public static int calculate(String s) { + if (s == null || s.length() == 0) return 0; + + Deque stack = new ArrayDeque<>(); + int num = 0; + char sign = '+'; + int len = s.length(); + for (int i = 0; i < len; i++) { + if (Character.isDigit(s.charAt(i))) { + num = num * 10 + s.charAt(i) - '0'; + } + if ((!Character.isDigit(s.charAt(i)) && ' ' != s.charAt(i)) || i == len - 1) { + if (sign == '-') { + stack.push(-num); + } + if (sign == '+') { + stack.push(num); + } + if (sign == '*') { + stack.push(stack.pop() * num); + } + if (sign == '/') { + stack.push(stack.pop() / num); + } + sign = s.charAt(i); + num = 0; + } + } + + int re = 0; + for (int i : stack) { + re += i; + } + return re; + } + + public static void main(String[] args) { + System.out.println(calculate("10-4+3*2+10/5")); + } +} diff --git a/src/main/java/practiceproblems/stack/DailyTemperature.java b/src/main/java/practiceproblems/stack/DailyTemperature.java new file mode 100644 index 0000000..973f466 --- /dev/null +++ b/src/main/java/practiceproblems/stack/DailyTemperature.java @@ -0,0 +1,62 @@ +package practiceproblems.stack; + +import java.util.ArrayDeque; +import java.util.Arrays; +import java.util.Deque; + +public class DailyTemperature { + public static int[] dailyTemperatures(int[] temperatures) { + if (temperatures.length == 1) return new int[]{0}; + Deque stack = new ArrayDeque<>(); + int[] result = new int[temperatures.length]; + stack.push(new Pair(temperatures[temperatures.length - 1], 0)); + result[temperatures.length - 1] = 0; + for (int i = temperatures.length - 2; i >= 0; i--) { + int count = 0; + while (!stack.isEmpty() && stack.peek().temp <= temperatures[i]) { + count += stack.pop().count; + } + if (!stack.isEmpty() && stack.peek().temp > temperatures[i]) count++; + + // for case [55,38,53,81,61,93,97,32,43,78] 97 comes in middle then we have to eliminate all entries + // but the count is zero + if (stack.isEmpty()) count = 0; + result[i] = count; + stack.push(new Pair(temperatures[i], count)); + } + + return result; + } + + public int[] dailyTemperaturesOptimised(int[] temperatures) { + Deque stack = new ArrayDeque<>(); + int[] ret = new int[temperatures.length]; + for (int i = 0; i < temperatures.length; i++) { + while (!stack.isEmpty() && temperatures[i] > temperatures[stack.peek()]) { + int idx = stack.pop(); + ret[idx] = i - idx; + } + stack.push(i); + } + return ret; + } + + public static void main(String[] args) { + int[][] inputs = new int[][]{{73, 74, 75, 71, 69, 72, 76, 73, + 30, 40, 50, 60, + 30, 60, 90, + 55, 38, 53, 81, 61, 93, 97, 32, 43, 78}}; + for (int[] input : inputs) + System.out.println(Arrays.toString(dailyTemperatures(input))); + } + + private static class Pair { + int temp; + int count; + + public Pair(int temp, int count) { + this.temp = temp; + this.count = count; + } + } +} diff --git a/src/main/java/practiceproblems/stack/DecodeString.java b/src/main/java/practiceproblems/stack/DecodeString.java new file mode 100644 index 0000000..ef55af9 --- /dev/null +++ b/src/main/java/practiceproblems/stack/DecodeString.java @@ -0,0 +1,51 @@ +package practiceproblems.stack; + +import java.util.ArrayDeque; +import java.util.Deque; + +// s = "3[a2[c]]", return "accaccacc". +// s = "2[abc]3[cd]ef", return "abcabccdcdcdef". +public class DecodeString { + public static String decodeString(String s) { + if (s == null) + return ""; + + Deque countStack = new ArrayDeque<>(); + Deque characterStack = new ArrayDeque<>(); + int start = 0; + StringBuilder result = new StringBuilder(); + while (start < s.length()) { + // whenever we see number, we push to countStack + if (Character.isDigit(s.charAt(start))) { + int num = 0; + while (Character.isDigit(s.charAt(start))) { + num = num * 10 + s.charAt(start) - '0'; + start++; + } + countStack.push(num); + } else if (s.charAt(start) == '[') { + // whenever we see a open brace we push the string we have to characterStack + characterStack.push(result.toString()); + result = new StringBuilder(); + start++; + } else if (s.charAt(start) == ']') { + // whenever a closing brace comes, we pop the last seen countStack and last seen string + // and replicate that string and stores in temp characterStack + StringBuilder sb = new StringBuilder(characterStack.pop()); + int tempCount = countStack.pop(); + sb.append(result.toString().repeat(tempCount)); + result = sb; + start++; + } else { + // whenever we see a char, we push to characterStack string + result.append(s.charAt(start++)); + } + } + + return result.toString(); + } + + public static void main(String[] args) { + System.out.println(decodeString("3[a2[c]]")); + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/stack/ExclusiveTIme.java b/src/main/java/practiceproblems/stack/ExclusiveTIme.java new file mode 100644 index 0000000..e4a0e2a --- /dev/null +++ b/src/main/java/practiceproblems/stack/ExclusiveTIme.java @@ -0,0 +1,46 @@ +package practiceproblems.stack; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.List; + +/** + * tricky revise TODO + * https://leetcode.com/problems/exclusive-time-of-functions/ + */ +public class ExclusiveTIme { + + public int[] exclusiveTime(int n, List logs) { + Deque stack = new ArrayDeque<>(); + int[] result = new int[n]; + for (String content : logs) { + Log log = new Log(content); + if (log.isStart) { + stack.push(log); + } else { + Log top = stack.pop(); + result[top.id] += (log.time - top.time + 1 - top.subDuration); + if (!stack.isEmpty()) { + stack.peek().subDuration += (log.time - top.time + 1); + } + } + } + + return result; + } + + public static class Log { + public int id; + public boolean isStart; + public int time; + public int subDuration; + + public Log(String content) { + String[] strs = content.split(":"); + id = Integer.parseInt(strs[0]); + isStart = strs[1].equals("start"); + time = Integer.parseInt(strs[2]); + subDuration = 0; + } + } +} diff --git a/src/main/java/practiceproblems/stack/MaxHistogram.java b/src/main/java/practiceproblems/stack/MaxHistogram.java new file mode 100644 index 0000000..491a55a --- /dev/null +++ b/src/main/java/practiceproblems/stack/MaxHistogram.java @@ -0,0 +1,104 @@ +package practiceproblems.stack; + +import java.util.ArrayDeque; +import java.util.Deque; + +/** + * https://leetcode.com/problems/largest-rectangle-in-histogram/ + */ + +public class MaxHistogram { + + /** + * Lets start by thinking of a brute force, naive solution. + * Pick two bars and find the maxArea between them and compare that to your global maxArea. + * To do that, you’ll need to find the bar that “restricts” the height of the forming rectangle to its own height - + * i.e; the bar with the minimum height between two bars. + * + * @param heights + * @return + */ + public int largestRectangleAreaBruteForce(int[] heights) { + if (heights.length == 1) return heights[0]; + int area = 0; + for (int i = 0; i < heights.length; i++) { + int min = heights[i]; + for (int j = i; j < heights.length; j++) { + min = Math.min(min, heights[j]); + area = Math.max(area, ((j - i + 1) * min)); + } + } + + return area; + } + + /** + * https://abhinandandubey.github.io/posts/2019/12/15/Largest-Rectangle-In-Histogram.html + * Because if the length of the array is n, the largest possible rectangle has to have a height one of the elements of the array, + * that is to say, there are only n possible largest rectangles. + * So we don't really need to go through every pair of bars, but should rather search by the height of the bar. + * + * Why Stack? + * At each step we need the information of previously seen "candidate" bars - bars which give us hope. + * These are the bars of increasing heights. + * And since they'll need to be put in the order of their occurrence, stack should come to your mind. + * + * Lets take the example [2, 1, 5, 6, 2, 3] + * + * The first bar we see is the bar at position 0 of height 2. + * It is definitely as "candidate bar" as it gives us hope of finding a larger rectangle, so we just add it to the stack. + * The next one we see is the bar at position 1 with height 1. + * At this point, we look at the stack and see that the "candidate bar" at the top of the stack is of height 2, + * and because of this 1, we definitely can't do a rectangle of height 2 now, + * so the only natural thing to do at this point is to pop it out of the stack, + * and see what area it could have given us. + * + * After adding 1,5,6 to the stack hoping to find bigger rectangle we hit at 2 which is smaller than 6 + * At this point it should be clear that we only pop from the stack when + * height of the current bar is lower than the height of the bar at the top of the stack. + * so lets see what it can give us and pop it out. + * The height of this rectangle is 6, and the width is i−stack[peek()]−1=> 4−2−1 => 1. + */ + public static int largestRectangleArea(int[] heights) { + if (heights.length == 1) return heights[0]; + int area = 0; + int result = 0; + Deque stack = new ArrayDeque<>(); + int i = 0; + while (i < heights.length) { + + if (stack.isEmpty() || heights[stack.peekLast()] <= heights[i]) { + stack.addLast(i++); + } else { + int top = stack.removeLast(); + if (stack.isEmpty()) { + area = heights[top] * i; + } else { + area = heights[top] * (i - stack.peekLast() - 1); + + } + + result = Math.max(result, area); + } + + } + + while (!stack.isEmpty()) { + int top = stack.removeLast(); + if (stack.isEmpty()) { + area = heights[top] * i; + } else { + area = heights[top] * (i - stack.peekLast() - 1); + } + result = Math.max(result, area); + } + + return result; + } + + + public static void main(String[] args) { + int[] arr = new int[]{2, 1, 5, 6, 2, 3}; + System.out.println(largestRectangleArea(arr)); + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/stack/MaxSubarrMinProduct.java b/src/main/java/practiceproblems/stack/MaxSubarrMinProduct.java new file mode 100644 index 0000000..58739d5 --- /dev/null +++ b/src/main/java/practiceproblems/stack/MaxSubarrMinProduct.java @@ -0,0 +1,99 @@ +package practiceproblems.stack; + +import java.util.ArrayDeque; +import java.util.Deque; + +public class MaxSubarrMinProduct { + /** + * The main idea is + * As we want to find min-product, calculating minimum number from all sub arrays gives us TLE + * So what we do is, we consider nums[i] where 0<=i< n, + * as min element and find the min-products of subarrays with minimum as nums[i]. + * + * left_max_index: index of the farthest element greater or equal to nums[i] in the left side + * right_max_index: index of the farthest element greater or equal to nums[i] in the right side + * + * Then we find the prefix sum of given input array. + * Then we find the number of largest numbers to the left in "array left" (left_max_index) + * and number of largest numbers to the right in "array right" with nums[i] as minimum. (right_max_index) + * + * After finding that, now its become simple, as we know the length of sub array with nums[i] as minimum. + * + * nums = [3,1,5,6,4,2] + *

+ * indices: [0, 1, 2, 3, 4, 5] + * input: [3, 1, 5, 6, 4, 2] + * left: [0, 0, 2, 3, 2, 2] + * right: [0, 5, 3, 3, 4, 5] + * prefixSum: [3, 4, 9, 15, 19, 21] + */ + public int maxSumMinProductEff(int[] nums) { + int n = nums.length; + long max = -1; + long[] prefix = new long[n + 1]; + for (int i = 1; i < prefix.length; i++) { + prefix[i] = prefix[i - 1] + nums[i - 1]; + } + Deque s1 = new ArrayDeque<>(); + Deque s2 = new ArrayDeque<>(); + int[] leftMaxIndex = new int[n]; + int[] rightMaxIndex = new int[n]; + for (int i = 0; i < n; i++) { + while (!s1.isEmpty() && nums[s1.peek()] >= nums[i]) { + s1.pop(); + } + if (s1.isEmpty()) { + leftMaxIndex[i] = -1; + } else { + leftMaxIndex[i] = s1.peek(); + } + s1.push(i); + } + for (int i = n - 1; i >= 0; i--) { + while (!s2.isEmpty() && nums[s2.peek()] >= nums[i]) { + s2.pop(); + } + if (s2.isEmpty()) { + rightMaxIndex[i] = n; + } else { + rightMaxIndex[i] = s2.peek(); + } + s2.push(i); + } + + for (int i = 0; i < n; i++) { + int leftIndex = leftMaxIndex[i]; + int rightIndex = rightMaxIndex[i]; + int min = nums[i]; + + // range sum technique + long subArraySum = prefix[rightIndex] - prefix[leftIndex + 1]; + long maxSubArrMinProduct = min * subArraySum; + max = Math.max(max, maxSubArrMinProduct); + } + return (int) (max % 1_000_000_007); + } + + + public static int maxSumMinProduct(int[] nums) { + int mod = (int) Math.pow(10, 9) + 7; + + int result = 0; + + for (int i = 0; i < nums.length; i++) { + int min = nums[i]; + int sum = nums[i]; + for (int j = i + 1; j < nums.length; j++) { + + min = Math.min(nums[j], min); + sum += nums[j]; + + result = Math.max(result, sum * min); + } + + System.out.println(result); + } + + return result % mod; + } +} diff --git a/src/main/java/practiceproblems/stack/NextGreaterElement.java b/src/main/java/practiceproblems/stack/NextGreaterElement.java new file mode 100644 index 0000000..b510aee --- /dev/null +++ b/src/main/java/practiceproblems/stack/NextGreaterElement.java @@ -0,0 +1,133 @@ +package practiceproblems.stack; + +import java.util.ArrayDeque; +import java.util.Arrays; +import java.util.Deque; +import java.util.HashMap; +import java.util.Stack; + +/** + * https://www.geeksforgeeks.org/next-greater-element/ + *

+ * https://www.geeksforgeeks.org/find-next-greater-number-set-digits/ + */ +class NextGreaterElement { + + static int arr[] = {1, 3, 4, 2}; + + // 9,1,2,3,4,5,6,7 + public static void printNGE() { + Stack s = new Stack<>(); + int[] nge = new int[arr.length]; + + for (int i = arr.length - 1; i >= 0; i--) { + + while (!s.empty() && s.peek() <= arr[i]) { + s.pop(); + } + nge[i] = s.empty() ? -1 : s.peek(); + s.push(arr[i]); + + } + for (int i = 0; i < arr.length; i++) + System.out.println(arr[i] + " --> " + nge[i]); + + } + + // 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. + public int[] nextGreaterElement(int[] findNums, int[] nums) { + int[] ret = new int[findNums.length]; + ArrayDeque stack = new ArrayDeque<>(); + HashMap map = new HashMap<>(); + for (int i = nums.length - 1; i >= 0; i--) { + while (!stack.isEmpty() && stack.peek() <= nums[i]) { + stack.pop(); + } + if (stack.isEmpty()) map.put(nums[i], -1); + else map.put(nums[i], stack.peek()); + stack.push(nums[i]); + } + for (int i = 0; i < findNums.length; i++) { + ret[i] = map.get(findNums[i]); + } + return ret; + } + + public int[] nextGreaterElementBruteForce(int[] nums1, int[] nums2) { + HashMap hash = new HashMap<>(); + for (int i = 0; i < nums2.length; i++) { + hash.put(nums2[i], i); + } + + int[] res = new int[nums1.length]; + int j; + + for (int i = 0; i < nums1.length; i++) { + for (j = hash.get(nums1[i]) + 1; j < nums2.length; j++) { + if (nums1[i] < nums2[j]) { + res[i] = nums2[j]; + break; + } + } + if (j == nums2.length) { + res[i] = -1; + } + } + + return res; + } + + // Input: [1,2,1] + // Output: [2,-1,2] + // Explanation: The first 1's next greater number is 2 + // The number 2 can't find next greater number; + // The second 1's next greater number needs to search circularly, which is also 2. + public static int[] nextGreaterElementCircular(int[] nums) { + if (nums == null || nums.length == 0) return new int[0]; + int[] result = new int[nums.length]; + Arrays.fill(result, -1); + Deque deque = new ArrayDeque<>(); + // to mimic the circular array we iterate for 2*n because input [1,2,1] will be like [1,2,1,1,2,1] + // and we take mod of 'n' to update the correct index + for (int i = 2 * nums.length - 1; i >= 0; --i) { + + while (!deque.isEmpty() && nums[deque.peek()] <= nums[i % nums.length]) { + deque.pop(); + } + // The stack is either empty, when no "greater element" is found to the right of nums[i], + // or contains the next greater element of nums[i] at the top. + result[i % nums.length] = deque.isEmpty() ? -1 : nums[deque.peek()]; + + // Push i into stack, so that nums[i-1] will compare with nums[i] first, before falling back to + // the next greater element of nums[i] + deque.push(i % nums.length); + } + + return result; + } + public int[] nextGreaterElementCircularBruteForce(int[] nums) { + int[] res = new int[nums.length]; + int[] doublenums = new int[nums.length * 2]; + System.arraycopy(nums, 0, doublenums, 0, nums.length); + System.arraycopy(nums, 0, doublenums, nums.length, nums.length); + for (int i = 0; i < nums.length; i++) { + res[i]=-1; + for (int j = i + 1; j < doublenums.length; j++) { + if (doublenums[j] > doublenums[i]) { + res[i] = doublenums[j]; + break; + } + } + } + return res; + } + + public static void main(String[] args) { + printNGE(); + } +} diff --git a/src/main/java/practiceproblems/stack/Pattern132.java b/src/main/java/practiceproblems/stack/Pattern132.java new file mode 100644 index 0000000..22315f5 --- /dev/null +++ b/src/main/java/practiceproblems/stack/Pattern132.java @@ -0,0 +1,74 @@ +package practiceproblems.stack; + +import java.util.ArrayDeque; +import java.util.Deque; + +public class Pattern132 { + + // record minimum till j and iterate from the end to find a max value which is + // greater the minimum(i) and smaller than j + // O(N^2) + public boolean find132pattern(int[] nums) { + int min = Integer.MAX_VALUE; // stores the min value till j + for (int j = 0; j < nums.length; j++) { + min = Math.min(nums[j], min); + if (min == nums[j]) continue; // if min and j are same move to next element + + for (int k = nums.length - 1; k > j; k--) { + if (min < nums[k] && nums[k] < nums[j]) return true; // condition to be satisfied + } + } + return false; + } + + /** + * Create a stack and initialize a variable second with INT_MIN value. + * Start traversing from the end of array. + * Check if the current number is lesser than second. If it is, then it means our 132 pattern is satisfied as the stack is taking care of the 32 pattern and the current number satisfies the entire pattern. So return true. + * If the above is not true, update the peak value in the stack. Keep popping from the stack until stack is empty OR the top value is greater than the current value. + * Push the current number into the stack. + * If the loop terminates, it means that the pattern was not found in the array. So, return false. + *

+ * Take the sample input as [3, 6, 4, 1, 2] + * Basically, the top of stack is containing the highest number so far, i.e. 3 and + * second is containing the second highest number after the highest number, i.e. 2. + * So, this satisfies the 32 pattern. Now, we will just keep updating second and stack top when we encounter a number + * which is greater than the highest number. + */ + public static boolean find132patternEffective(int[] nums) { + Deque maxStack = new ArrayDeque<>(); + int minValue = Integer.MIN_VALUE; + + for (int i = nums.length - 1; i >= 0; i--) { + if (nums[i] < minValue) return true; + while (!maxStack.isEmpty() && maxStack.peek() < nums[i]) { + minValue = maxStack.pop(); + } + maxStack.push(nums[i]); + } + + return false; + } + + public boolean find132patternMinArr(int[] nums) { + int n = nums.length; + int[] mins = new int[n]; + mins[0] = nums[0]; + for (int i = 1; i < n - 1; i++) { + mins[i] = Math.min(nums[i], mins[i - 1]); + } + Deque stk = new ArrayDeque<>(); + for (int i = n - 1; i > 0; i--) { + while (!stk.isEmpty() && nums[i] > stk.peek()) { + if (stk.peek() > mins[i - 1]) return true; + stk.pop(); + } + stk.push(nums[i]); + } + return false; + } + + public static void main(String[] args) { + System.out.println(find132patternEffective(new int[]{3, 5, 0, 3, 4})); + } +} diff --git a/src/main/java/practiceproblems/stack/RemoveKDigits.java b/src/main/java/practiceproblems/stack/RemoveKDigits.java new file mode 100644 index 0000000..c77381a --- /dev/null +++ b/src/main/java/practiceproblems/stack/RemoveKDigits.java @@ -0,0 +1,60 @@ +package practiceproblems.stack; + +import java.util.ArrayDeque; +import java.util.Deque; + +/** + * https://leetcode.com/problems/remove-k-digits/ + * + * Given a non-negative integer num represented as a string, + * remove k digits from the number so that the new number is the smallest possible. + * + * Note: + * The length of num is less than 10002 and will be ≥ k. + * The given num does not contain any leading zero. + * + * Input: num = "1432219", k = 3 + * Output: "1219" + * Explanation: Remove the three digits 4, 3, and 2 to form the new number 1219 which is the smallest. + * + * Input: num = "10", k = 2 + * Output: "0" + * Explanation: Remove all the digits from the number and it is left with nothing which is 0. + */ + +public class RemoveKDigits { + //1432219 + public static String removeKdigits(String num, int k) { + if(num==null || num.length()==0) return null; + + Deque queue= new ArrayDeque<>(); + for(char c: num.toCharArray()){ + + while(!queue.isEmpty() && queue.getLast()>c-'0' && k>0){ + queue.removeLast(); + k--; + } + queue.addLast(c-'0'); + } + + while(k>0){ + queue.removeLast(); + k--; + } + + StringBuilder result= new StringBuilder(); + while(!queue.isEmpty()){ + result.append(queue.removeFirst()); + } + + while(result.length()>0 && result.charAt(0)=='0'){ + result.deleteCharAt(0); + } + + return result.length()==0?"0":result.toString(); + } + + public static void main(String[] args) { + System.out.println(removeKdigits("14232191", 3)); + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/stack/SmallestLexicoSubSeq.java b/src/main/java/practiceproblems/stack/SmallestLexicoSubSeq.java new file mode 100644 index 0000000..88062c5 --- /dev/null +++ b/src/main/java/practiceproblems/stack/SmallestLexicoSubSeq.java @@ -0,0 +1,46 @@ +package practiceproblems.stack; + +import java.util.ArrayDeque; +import java.util.Deque; + +/** + * https://leetcode.com/problems/smallest-subsequence-of-distinct-characters/ + * https://leetcode.com/problems/remove-duplicate-letters/ + */ +public class SmallestLexicoSubSeq { + + /** + * Question is asking sequence without duplicates but in sorted order + * The Characters Which are unique will remain in the same order. + * If we have pushed a unique character then there is no benefit of changing element before unique character. + * + * So we process character by character in to a stack + * if we find a character which is smaller that stack's peek, we check if we are going to encounter it again by having lastPos array + * |--> if yes then we pop from stack and remove from used arr, so we can process the duplicate which will give smaller string + * + */ + public String smallestSubsequence(String s) { + int[] lastPos = new int[26]; + boolean[] used = new boolean[26]; + int n = s.length(); + for (int i = 0; i < n; i++) + lastPos[s.charAt(i) - 'a'] = i; + + Deque stack = new ArrayDeque<>(); + for (int i = 0; i < n; i++) { + int c = s.charAt(i) - 'a'; + if (used[c]) continue; + // if stack has larger value and lastPos of that larger value is not yet reached we pop and place smaller char + while (!stack.isEmpty() && stack.peekLast() >= c && lastPos[stack.peekLast()] > i) { + used[stack.pollLast()] = false; + } + used[c] = true; + stack.push(c); + } + + StringBuilder sb = new StringBuilder(); + while (!stack.isEmpty()) + sb.append((char) (stack.poll() + 'a')); + return sb.toString(); + } +} diff --git a/src/main/java/practiceproblems/stack/SortStack.java b/src/main/java/practiceproblems/stack/SortStack.java new file mode 100644 index 0000000..55b0085 --- /dev/null +++ b/src/main/java/practiceproblems/stack/SortStack.java @@ -0,0 +1,19 @@ +package practiceproblems.stack; + +import java.util.Stack; + +public class SortStack { + // Input : [34, 3, 31, 98, 92, 23] + // Output : [3, 23, 31, 34, 92, 98] + public static void stackSorting(Stack stack) { + Stack tempStack = new Stack<>(); + while (!stack.isEmpty()) { + int item = stack.pop(); + while (!tempStack.isEmpty() && tempStack.peek() > item) { + stack.push(tempStack.pop()); + } + tempStack.push(item); + } + while (!tempStack.isEmpty()) stack.push(tempStack.pop()); + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/stack/StockSpanner.java b/src/main/java/practiceproblems/stack/StockSpanner.java new file mode 100644 index 0000000..f191b0e --- /dev/null +++ b/src/main/java/practiceproblems/stack/StockSpanner.java @@ -0,0 +1,27 @@ +package practiceproblems.stack; + + +import graph.leetcode.Pair; + +import java.util.ArrayDeque; +import java.util.Deque; + +class StockSpanner { + + Deque> stack; + + public StockSpanner() { + this.stack = new ArrayDeque<>(); + } + + public int next(int price) { + int value = 1; + while (!stack.isEmpty() && stack.peek().getKey() <= price) { + value += stack.pop().getValue(); + } + + stack.push(new Pair(price, value)); + + return stack.peek().getValue(); + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/stack/SumOfMinSubArrays.java b/src/main/java/practiceproblems/stack/SumOfMinSubArrays.java new file mode 100644 index 0000000..30c3c0a --- /dev/null +++ b/src/main/java/practiceproblems/stack/SumOfMinSubArrays.java @@ -0,0 +1,112 @@ +package practiceproblems.stack; + +import java.util.ArrayDeque; +import java.util.Arrays; +import java.util.Deque; + +/** + * https://leetcode.com/problems/sum-of-subarray-minimums/ + *

+ * https://www.youtube.com/watch?v=Ulb3ixSpE4Y&ab_channel=AnuragCodes + */ +public class SumOfMinSubArrays { + + public static void main(String[] args) { + sumSubarrayMins(new int[]{5, 3, 4, 1, 2, 7}); + } + + public static int sumSubarrayMins(int[] arr) { + int length = arr.length; + long mod = (long) 1e9 + 7; + long result = 0; + Deque previousLess = new ArrayDeque<>(); + Deque nextLess = new ArrayDeque<>(); + int[] left = new int[length]; + int[] right = new int[length]; + // previous less element - imagine 2, 5, 6, 5 you don't want to double count so you can either enforce the = on the left or right array. + for (int i = 0; i < length; i++) { + while (!previousLess.isEmpty() && previousLess.peek()[0] >= arr[i]) { + previousLess.pop(); + } + // i - previousLess.peek()[1] similar to histogram problem + //For example [7 8 4 3], there is no Prev Left Element for element 4, so left[2] = 2+1 =3. + left[i] = previousLess.isEmpty() ? i + 1 : i - previousLess.peek()[1]; + previousLess.push(new int[]{arr[i], i}); + } + // left[i] corresponds => for the subarray size left[i] the current element arr[i] is the minimum + // the arr[3]=>1 is having left[i]=4 means for the subarray length size 4 to the left, 1 is minimum; + // [5, 3, 4, 1, 2, 7] + // [1, 2, 1, 4, 1, 1] + System.out.println(Arrays.toString(left)); + // next less element. for this we do reverse traversal + for (int i = length - 1; i >= 0; i--) { + while (!nextLess.isEmpty() && nextLess.peek()[0] > arr[i]) { + nextLess.pop(); + } + right[i] = nextLess.isEmpty() ? length - i : nextLess.peek()[1] - i; + nextLess.push(new int[]{arr[i], i}); + } + System.out.println(Arrays.toString(right)); + + /** + * Generally there are (N*N+1)/2 sub arrays in a given array, + * to find out the number of sub arrays the arr[i] to be present in + * we calculate using arr[i]*leftLength*rightLength + * + * example: [5, 3, 4, 1, 2, 7] total subarrays = 6*(6+1)/2 = 21 + * excluding 1 from above we have the left part [5,3,4] => 3*4/2 = 6 + * excluding 1 from above we have the right part [2,7] => 2*3/2 = 3 + * so out of 21, the arr[3]= 1 will be part of only 21-6+3 sub arrays = 12 + * + * the reason to multiply arr[i] is to get total sum + * + * How much the element 2 contributes to the final answer? + * Since we want to add the minimum from each subarray. + * In this case in the 12 subarrays, 1 is the minimum, so it will contribute 1*12 i.e. 12. + * if the min is 2 then it will be 2*12= 24 + */ + for (int i = 0; i < length; i++) { + result = (result + (long) arr[i] * left[i] * right[i]) % mod; + } + return (int) result; + } + + public int sumSubarrayMinsTLE(int[] arr) { + if (arr.length == 1) return arr[0]; + int mod = (int) Math.pow(10, 9) + 7; + + int result = 0; + for (int i = 0; i < arr.length; i++) { + int min = arr[i]; + for (int j = i; j < arr.length; j++) { + min = Math.min(min, arr[j]); + result += min; + result %= mod; + } + } + return result; + + } + + public int sumSubarrayMinsMLE(int[] arr) { + if (arr.length == 1) return arr[0]; + int mod = (int) Math.pow(10, 9) + 7; + int[][] dp = new int[arr.length][arr.length]; + int result = 0; + for (int i = 0; i < arr.length; i++) { + dp[i][i] = arr[i]; + result += dp[i][i]; + } + + for (int l = 1; l < arr.length; l++) { + for (int i = 0; i < arr.length - l; i++) { + int j = i + l; + dp[i][j] = Math.min(dp[i + 1][j] % mod, dp[i][j - 1] % mod); + result += dp[i][j]; + result %= mod; + } + } + + return result; + } +} diff --git a/src/main/java/practiceproblems/stack/WaterTrapping.java b/src/main/java/practiceproblems/stack/WaterTrapping.java new file mode 100644 index 0000000..e45d5db --- /dev/null +++ b/src/main/java/practiceproblems/stack/WaterTrapping.java @@ -0,0 +1,91 @@ +package practiceproblems.stack; + +/** + * pseudocode : GetTotalWater(H) + * total <- 0 + * for every i in H + * leftMax <- findMax(0,i); + * rightMax <- findMax(i,n); + * permissibleWaterStored <- min(leftMax,rightMax); + * waterStored <- permissibleWaterStored-H[i]; + * total <- total+waterStored; + * return total; + */ +class WaterTrapping { + + public static int trap(int[] height) { + + int[] maxLeft = new int[height.length]; + int[] maxRight = new int[height.length]; + int maxHeight = 0; + int minHeight = 0; + + for (int i = 0; i < height.length; i++) { + maxHeight = Math.max(maxHeight, height[i]); + maxLeft[i] = maxHeight; + + } + maxHeight = 0; + for (int i = height.length - 1; i >= 0; i--) { + maxHeight = Math.max(maxHeight, height[i]); + maxRight[i] = maxHeight; + } + int result = 0; + for (int i = 0; i < height.length; i++) { + minHeight = Math.min(maxLeft[i], maxRight[i]); + height[i] = Math.max(0, minHeight - height[i]); + result += height[i]; + } + + return result; + + } + + /** + * https://www.youtube.com/watch?v=EdR3V5DBgyo + * + * @param height + * @return + */ + public int trapAnother(int[] height) { + if (height == null || height.length == 0) { + return 0; + } + int left = 0; + int right = height.length - 1; // Pointers to both ends of the array. + int maxLeft = 0; + int maxRight = 0; + + int totalWater = 0; + while (left < right) { + // Water could, potentially, fill everything from left to right, if there is nothing in between. + if (height[left] <= height[right]) { + // If the current elevation is greater than the previous maximum, water cannot occupy that point at all. + // However, we do know that everything from maxLeft to the current index, has been optimally filled, as we've + // been adding water to the brim of the last maxLeft. + maxLeft = Math.max(maxLeft, height[left]); + totalWater += maxLeft - height[left]; + + // Increment left, we'll now look at the next point. + left++; + // If the height at the left is NOT greater than height at the right, we cannot fill from left to right without over- + // flowing; however, we do know that we could potentially fill from right to left, if there is nothing in between. + } else { + // Similarly to above, we see that we've found a height greater than the max, and cannot fill it whatsoever, but + // everything before is optimally filled + maxRight = Math.max(maxRight, height[right]); + totalWater += maxRight - height[right]; + + // Decrement left, we'll look at the next point. + right--; + } + } + // Return the sum we've been adding to. + return totalWater; + } + + public static void main(String[] args) { + int[] arr = {0, 1, 0, 2, 1, 0, 1, 3, 2, 1}; + System.out.println("Maximum water that " + "can be accumulated is " + trap(arr)); + } +} \ No newline at end of file diff --git a/src/main/java/practiceproblems/sweepline/MaxSumRangeQuery.java b/src/main/java/practiceproblems/sweepline/MaxSumRangeQuery.java new file mode 100644 index 0000000..0de998d --- /dev/null +++ b/src/main/java/practiceproblems/sweepline/MaxSumRangeQuery.java @@ -0,0 +1,75 @@ +package practiceproblems.sweepline; + +import java.util.Arrays; + +/** + * https://leetcode.com/problems/maximum-sum-obtained-of-any-permutation + *

+ * Idea is to find the frequencies/(number of times an index appear + * in the requests) and then keep the maximum number from input array + * in the max frequency index and so on. + *

+ * Example: input array [1,2,3,4,5] requests [[1,3],[0,1]] + * frequencyOfRequests =[1,2,1,1,0]; + *

+ * Since we are trying to get maximum sum we have to position max number in the input array in the index such that, + * index has maximum frequency. + * Now sort the input array and frequencyOfRequests so that we will have max number and max frequency in first indexes. + * After sorting: + * input array = [5,4,3,2,1] + * frequencyOfRequests =[2,1,1,1,0]; + *

+ * for i in input array { + * sum += inputArray[i] * frequencyOfRequests[i] + * since sum can be very large and can cause overflow int; sum = sum % (10^9 + 7); + * }; + *

+ * frequencyOfRequests can be found by iterating every request from start to end and mark the inputArray[index]++; in this case Time = * O(inputArray.size) * O(request.size) but with marking start index with +1 and (end + 1) index with -1 Time = (requests.size) * (1); + *

+ * Time - r + n + nlog(n) + flog(f) + n -> O(nlog(n)) + * Space - O(n) for storing frequencies. + */ +public class MaxSumRangeQuery { + + public static int maxSumRangeQuery(int[] nums, int[][] requests) { + + int[] freq = new int[nums.length]; + long mod = (long) 1e9 + 7; + long res = 0; + int n = nums.length; + /* + Mark start and ends of requests with +1 and -1 + Imagine that you had intervals [1:3], [3:5]. + For each mark boundaries only to avoid complexity raise (TLE) on a wide interval as you do nothing with values + inside the interval and ignore it quantity. + t[start] +=1 means every index after this one will be counted 1 more time, + t[end+1] -= 1, means every index this one will be counted 1 less time + after [1:3] : [0, 1(+1), 0, 0, -1(-1), 0, 0] + after [3:5] : [0, 1, 0, 1(+1), -1, 0, -1(-1)] + Then after all intervals start|end+1 marking we can set the final frequency in one pass. + So the prefix sum of t will correspond to the number of requests for each index. + for i in range(1, n): + count[i] += count[i - 1] + It will become + [0, 1, 0, 1, -1, 0, -1] -> [0, 1, 1, 2, 1, 1, 0] + As you can see 3rd element occurred 2 times 1,2,4,5 1 time and others 0 time + */ + for (int[] r : requests) { + freq[r[0]] += 1; + if (r[1] + 1 < n) + freq[r[1] + 1] -= 1; + } + for (int i = 1; i < n; ++i) + freq[i] += freq[i - 1]; + Arrays.sort(nums); + Arrays.sort(freq); + for (int i = 0; i < n; i++) + res += (long) nums[i] * freq[i]; + return (int) (res % mod); + + } + + public static void main(String[] args) { + maxSumRangeQuery(new int[]{1, 2, 3, 4, 5}, new int[][]{{1, 3}, {0, 1}}); + } +} diff --git a/src/main/java/practiceproblems/sweepline/ModifyArray.java b/src/main/java/practiceproblems/sweepline/ModifyArray.java new file mode 100644 index 0000000..2152170 --- /dev/null +++ b/src/main/java/practiceproblems/sweepline/ModifyArray.java @@ -0,0 +1,42 @@ +package practiceproblems.sweepline; + +/** + * https://leetcode.com/problems/range-addition/ + * https://leetcode.com/problems/corporate-flight-bookings/ + * + * e.g. n =5 , [1,3,2] [2,4,3] [0,2,-2] + * + * idx: 0 1 2 3 4 + * nbr: 1 2 3 4 5 + * -------------------------------------------------------- + * [1,3,2] +2 -2 + * [2,4,3] +3 -3 + * [0,2,-2] -2 +2 + * -------------------------------------------------------- + * Sum: -2 2 3 2 -2 + * PrefixSum: -2 0 3 5 3 + */ +public class ModifyArray { + + public int[] getModifiedArray(int length, int[][] updates) { + + int[] result = new int[length]; + + for (int[] update : updates) { + int val = update[2]; + int start = update[0]; + int end = update[1]; + + result[start] += val; + if (end < length - 1) { + result[end + 1] -= val; + } + } + + + for (int i = 1; i < length; i++) { + result[i] += result[i - 1]; + } + return result; + } +} diff --git a/src/main/java/practiceproblems/tries/LongestRepeatingSubstring.java b/src/main/java/practiceproblems/tries/LongestRepeatingSubstring.java new file mode 100644 index 0000000..43d1b63 --- /dev/null +++ b/src/main/java/practiceproblems/tries/LongestRepeatingSubstring.java @@ -0,0 +1,37 @@ +package practiceproblems.tries; + +/** + * https://leetcode.com/problems/longest-repeating-substring + * // revise tricky + */ +public class LongestRepeatingSubstring { + + + public int longestRepeatingSubstring(String S) { + char[] A = S.toCharArray(); + int res = 0; + TrieNode root = new TrieNode(); + for (int i = 0; i < S.length(); i++) { + TrieNode cur = root; + for (int j = i; j < S.length(); j++) { + if (cur.next[A[j] - 'a'] == null) { + TrieNode newNode = new TrieNode(); + cur.next[A[j] - 'a'] = newNode; + cur = newNode; + } else { + res = Math.max(res, j - i + 1); + cur = cur.next[A[j] - 'a']; + } + } + } + return res; + } + + class TrieNode { + TrieNode[] next; + + public TrieNode() { + next = new TrieNode[26]; + } + } +} diff --git a/src/main/java/practiceproblems/tries/MapSum.java b/src/main/java/practiceproblems/tries/MapSum.java new file mode 100644 index 0000000..f688b27 --- /dev/null +++ b/src/main/java/practiceproblems/tries/MapSum.java @@ -0,0 +1,102 @@ +package practiceproblems.tries; + +import java.util.HashMap; +import java.util.Map; + +/** + * https://leetcode.com/problems/map-sum-pairs/ + * https://www.youtube.com/watch?v=AaMIYNdOz8w&ab_channel=NareshGupta + * + * brute force approach is add all the key value to a map + * + * for every sum(prefix) call: + * iterate -> map + * -> map.key.startsWith(prefix) add it to sum + * -> return sum + */ +public class MapSum { + + TrieNode root; + /** + * Initialize your data structure here. + */ + Map sumStorage; + + public MapSum() { + root = new TrieNode(); + sumStorage = new HashMap<>(); + } + + /** + * sumStorage is used to track the changes of the existing keys + * + * this is going to update all the chars in the path with value apple,5 + * diff is initially 5 + * apple -> 5 + * a -> 5 + * | + * ap -> 5 + * | + * app -> 5 + * | + * appl -> 5 + * | + * apple -> 5 + * + * if we get a new key app,2 + * diff will be 2 initially + * a -> 5+2 + * | + * p -> 5+2 + * | + * ap -> 5 +2 + * | + * app -> 5 + 2 + * | + * appl -> 5 + * | + * apple -> 5 + * + * after some time if we get apple , 3 + * we get difference as 3-(map.get('apple')) = 3-5 = -2 + * we add the diff to all the elements + */ + + + + public void insert(String key, int val) { + TrieNode head = root; + int diff = val - sumStorage.getOrDefault(key, 0); + sumStorage.put(key, val); + + for (char c : key.toCharArray()) { + head.children.putIfAbsent(c, new TrieNode()); + head = head.children.get(c); + head.value += diff; + } + } + + public int sum(String prefix) { + TrieNode head = root; + for (char c : prefix.toCharArray()) { + if (head.children.get(c) == null) return 0; + head = head.children.get(c); + + } + return head.value; + } + + private static class TrieNode { + Map children; + boolean isWord; + int value; + + public TrieNode() { + isWord = false; + children = new HashMap<>(); + value = 0; + } + } + +} + diff --git a/src/main/java/practiceproblems/tries/WordDictionary.java b/src/main/java/practiceproblems/tries/WordDictionary.java new file mode 100644 index 0000000..cd60efc --- /dev/null +++ b/src/main/java/practiceproblems/tries/WordDictionary.java @@ -0,0 +1,67 @@ +package practiceproblems.tries; + +/** + * Design a data structure that supports the following two operations: + * void addWord(word) + * bool search(word) + * search(word) can search a literal word or + * a regular expression string containing only letters a-z or .. A . + * means it can represent any one letter. + * addWord("bad") + * addWord("dad") + * addWord("mad") + * search("pad") -> false + * search("bad") -> true + * search(".ad") -> true + * search("b..") -> true + */ +public class WordDictionary { + TrieNode root; + + public WordDictionary() { + this.root = new TrieNode(); + } + + public void addWord(String word) { + TrieNode head = root; + for (int i = 0; i < word.length(); i++) { + if (head.children[word.charAt(i) - 'a'] == null) head.children[word.charAt(i) - 'a'] = new TrieNode(); + head = head.children[word.charAt(i) - 'a']; + } + + head.isWord = true; + + } + + public boolean search(String word) { + + return searchUtil(word.toCharArray(), 0, root); + } + + public boolean searchUtil(char[] words, int start, TrieNode root) { + if (start == words.length) return root.isWord; + + if (words[start] == '.') { + for (int i = 0; i < 26; i++) { + if (root.children[i] != null && searchUtil(words, start + 1, root.children[i])) { + return true; + } + } + } else { + return root.children[words[start] - 'a'] != null && searchUtil(words, start + 1, root.children[words[start] - 'a']); + } + + return false; + } + + static class TrieNode { + boolean isWord; + TrieNode[] children; + + public TrieNode() { + isWord = false; + children = new TrieNode[26]; + } + } +} + diff --git a/src/main/java/reflections/annotation/Main.java b/src/main/java/reflections/annotation/Main.java new file mode 100644 index 0000000..d83033b --- /dev/null +++ b/src/main/java/reflections/annotation/Main.java @@ -0,0 +1,137 @@ +package reflections.annotation; + +import reflections.annotation.annotations.Annotations; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.*; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + +/** + * Repeatable Annotations + * https://www.udemy.com/course/java-reflection-master-class + */ +@Annotations.ScanPackages({"loaders"}) +public class Main { + public static void main(String[] args) throws Throwable { + schedule(); + } + + public static void schedule() throws ClassNotFoundException, IOException, URISyntaxException { + Annotations.ScanPackages scanPackages = Main.class.getAnnotation(Annotations.ScanPackages.class); + if (scanPackages == null || scanPackages.value().length == 0) { + return; + } + + List> allClasses = getAllClasses(scanPackages.value()); + List scheduledExecutorMethods = getScheduledExecutorMethods(allClasses); + + for (Method method : scheduledExecutorMethods) { + scheduleMethodExecution(method); + } + } + + private static void scheduleMethodExecution(Method method) { + Annotations.ExecuteOnSchedule[] schedules = method.getAnnotationsByType(Annotations.ExecuteOnSchedule.class); + + ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(); + + for (Annotations.ExecuteOnSchedule schedule : schedules) { + scheduledExecutorService.scheduleAtFixedRate( + () -> runWhenScheduled(method), + schedule.delaySeconds(), + schedule.periodSeconds(), + TimeUnit.SECONDS); + } + } + + private static void runWhenScheduled(Method method) { + Date currentDate = new Date(); + SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss"); + + System.out.println(String.format("Executing at %s", dateFormat.format(currentDate))); + + try { + method.invoke(null); + } catch (IllegalAccessException | InvocationTargetException e) { + e.printStackTrace(); + } + } + + private static List getScheduledExecutorMethods(List> allClasses) { + List scheduledMethods = new ArrayList<>(); + + for (Class clazz : allClasses) { + if (!clazz.isAnnotationPresent(Annotations.ScheduledExecutorClass.class)) { + continue; + } + for (Method method : clazz.getDeclaredMethods()) { + if (method.getAnnotationsByType(Annotations.ExecuteOnSchedule.class).length != 0) { + scheduledMethods.add(method); + } + } + } + return scheduledMethods; + } + + public static List> getAllClasses(String... packageNames) throws URISyntaxException, IOException, ClassNotFoundException { + List> allClasses = new ArrayList<>(); + + for (String packageName : packageNames) { + String packageRelativePath = packageName.replace('.', '/'); + + URI packageUri = Main.class.getResource(packageRelativePath).toURI(); + + if (packageUri.getScheme().equals("file")) { + Path packageFullPath = Paths.get(packageUri); + allClasses.addAll(getAllPackageClasses(packageFullPath, packageName)); + } else if (packageUri.getScheme().equals("jar")) { + FileSystem fileSystem = FileSystems.newFileSystem(packageUri, Collections.emptyMap()); + + Path packageFullPathInJar = fileSystem.getPath(packageRelativePath); + allClasses.addAll(getAllPackageClasses(packageFullPathInJar, packageName)); + + fileSystem.close(); + } + } + return allClasses; + } + + private static List> getAllPackageClasses(Path packagePath, String packageName) throws IOException, ClassNotFoundException { + + if (!Files.exists(packagePath)) { + return Collections.emptyList(); + } + + List files = Files.list(packagePath) + .filter(Files::isRegularFile) + .collect(Collectors.toList()); + + List> classes = new ArrayList<>(); + + for (Path filePath : files) { + String fileName = filePath.getFileName().toString(); + + if (fileName.endsWith(".class")) { + String classFullName = packageName.isBlank() + ? fileName.replaceFirst("\\.class$", "") + : packageName + "." + fileName.replaceFirst("\\.class$", ""); + + Class clazz = Class.forName(classFullName); + classes.add(clazz); + } + } + return classes; + } +} diff --git a/src/main/java/reflections/annotation/annotations/Annotations.java b/src/main/java/reflections/annotation/annotations/Annotations.java new file mode 100644 index 0000000..0258051 --- /dev/null +++ b/src/main/java/reflections/annotation/annotations/Annotations.java @@ -0,0 +1,33 @@ + + +package reflections.annotation.annotations; + +import java.lang.annotation.*; + +public class Annotations { + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE) + public @interface ScanPackages { + String[] value(); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.TYPE) + public @interface ScheduledExecutorClass { + } + + @Repeatable(ExecutionSchedules.class) + @Target(ElementType.METHOD) + public @interface ExecuteOnSchedule { + int delaySeconds() default 0; + + int periodSeconds(); + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.METHOD) + public @interface ExecutionSchedules { + ExecuteOnSchedule[] value(); + } +} diff --git a/src/main/java/reflections/annotation/loaders/Cache.java b/src/main/java/reflections/annotation/loaders/Cache.java new file mode 100644 index 0000000..e2669ee --- /dev/null +++ b/src/main/java/reflections/annotation/loaders/Cache.java @@ -0,0 +1,17 @@ + + +package reflections.annotation.loaders; + +import static reflections.annotation.annotations.Annotations.ExecuteOnSchedule; +import static reflections.annotation.annotations.Annotations.ScheduledExecutorClass; + + +@ScheduledExecutorClass +public class Cache { + + @ExecuteOnSchedule(periodSeconds = 5) + @ExecuteOnSchedule(delaySeconds = 10, periodSeconds = 1) + public static void reloadCache() { + System.out.println("Reloading cache"); + } +} diff --git a/src/main/java/reflections/arrays/Main.java b/src/main/java/reflections/arrays/Main.java new file mode 100644 index 0000000..06d462d --- /dev/null +++ b/src/main/java/reflections/arrays/Main.java @@ -0,0 +1,129 @@ + + +package reflections.arrays; + +import reflections.arrays.data.Actor; +import reflections.arrays.data.Movie; + +import java.lang.reflect.Array; +import java.lang.reflect.Field; + +/** + * Json Write With Arrays + * https://www.udemy.com/course/java-reflection-master-class + */ +public class Main { + public static void main(String[] args) throws IllegalAccessException { + Actor actor1 = new Actor("Elijah Wood", new String[]{"Lord of the Rings", "The Good Son"}); + Actor actor2 = new Actor("Ian McKellen", new String[]{"X-Men", "Hobbit"}); + Actor actor3 = new Actor("Orlando Bloom", new String[]{"Pirates of the Caribbean", "Kingdom of Heaven"}); + + Movie movie = new Movie("Lord of the Rings", 8.8f, new String[]{"Action", "Adventure", "Drama"}, + new Actor[]{actor1, actor2, actor3}); + + String json = objectToJson(movie, 0); + + System.out.println(json); + } + + public static String objectToJson(Object instance, int indentSize) throws IllegalAccessException { + Field[] fields = instance.getClass().getDeclaredFields(); + StringBuilder stringBuilder = new StringBuilder(); + + stringBuilder.append(indent(indentSize)); + stringBuilder.append("{"); + stringBuilder.append("\n"); + + for (int i = 0; i < fields.length; i++) { + Field field = fields[i]; + field.setAccessible(true); + + if (field.isSynthetic()) { + continue; + } + + stringBuilder.append(indent(indentSize + 1)); + stringBuilder.append(formatStringValue(field.getName())); + + stringBuilder.append(":"); + + if (field.getType().isPrimitive()) { + stringBuilder.append(formatPrimitiveValue(field.get(instance), field.getType())); + } else if (field.getType().equals(String.class)) { + stringBuilder.append(formatStringValue(field.get(instance).toString())); + } else if (field.getType().isArray()) { + stringBuilder.append(arrayToJson(field.get(instance), indentSize + 1)); + } else { + stringBuilder.append(objectToJson(field.get(instance), indentSize + 1)); + } + + if (i != fields.length - 1) { + stringBuilder.append(","); + } + stringBuilder.append("\n"); + } + + stringBuilder.append(indent(indentSize)); + stringBuilder.append("}"); + return stringBuilder.toString(); + } + + private static String arrayToJson(Object arrayInstance, int indentSize) throws IllegalAccessException { + StringBuilder stringBuilder = new StringBuilder(); + + int arrayLength = Array.getLength(arrayInstance); + + Class componentType = arrayInstance.getClass().getComponentType(); + + stringBuilder.append("["); + stringBuilder.append("\n"); + + for (int i = 0; i < arrayLength; i++) { + Object element = Array.get(arrayInstance, i); + + if (componentType.isPrimitive()) { + stringBuilder.append(indent(indentSize + 1)); + stringBuilder.append(formatPrimitiveValue(element, componentType)); + } else if (componentType.equals(String.class)) { + stringBuilder.append(indent(indentSize + 1)); + stringBuilder.append(formatStringValue(element.toString())); + } else { + stringBuilder.append(objectToJson(element, indentSize + 1)); + } + + if (i != arrayLength - 1) { + stringBuilder.append(","); + } + stringBuilder.append("\n"); + } + + stringBuilder.append(indent(indentSize)); + stringBuilder.append("]"); + return stringBuilder.toString(); + } + + private static String indent(int indentSize) { + StringBuilder stringBuilder = new StringBuilder(); + for (int i = 0; i < indentSize; i++) { + stringBuilder.append("\t"); + } + return stringBuilder.toString(); + } + + private static String formatPrimitiveValue(Object instance, Class type) { + if (type.equals(boolean.class) + || type.equals(int.class) + || type.equals(long.class) + || type.equals(short.class)) { + return instance.toString(); + } else if (type.equals(double.class) || type.equals(float.class)) { + return String.format("%.02f", instance); + } + + throw new RuntimeException(String.format("Type : %s is unsupported", type.getTypeName())); + } + + private static String formatStringValue(String value) { + return String.format("\"%s\"", value); + } +} diff --git a/src/main/java/reflections/arrays/data/Actor.java b/src/main/java/reflections/arrays/data/Actor.java new file mode 100644 index 0000000..83b9e94 --- /dev/null +++ b/src/main/java/reflections/arrays/data/Actor.java @@ -0,0 +1,13 @@ + + +package reflections.arrays.data; + +public class Actor { + private final String name; + private final String[] knownForMovies; + + public Actor(String name, String[] knownForMovies) { + this.name = name; + this.knownForMovies = knownForMovies; + } +} diff --git a/src/main/java/reflections/arrays/data/Movie.java b/src/main/java/reflections/arrays/data/Movie.java new file mode 100644 index 0000000..808271f --- /dev/null +++ b/src/main/java/reflections/arrays/data/Movie.java @@ -0,0 +1,17 @@ + + +package reflections.arrays.data; + +public class Movie { + private final String name; + private final float rating; + private final String[] categories; + private final Actor[] actors; + + public Movie(String name, float rating, String[] categories, Actor[] actors) { + this.name = name; + this.rating = rating; + this.categories = categories; + this.actors = actors; + } +} diff --git a/src/main/java/reflections/configparser/configloader/ConfigLoaderLibrary.java b/src/main/java/reflections/configparser/configloader/ConfigLoaderLibrary.java new file mode 100644 index 0000000..3e8d28c --- /dev/null +++ b/src/main/java/reflections/configparser/configloader/ConfigLoaderLibrary.java @@ -0,0 +1,102 @@ + + +package reflections.configparser.configloader; + +import reflections.configparser.data.GameConfig; + +import java.io.IOException; +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.nio.file.Path; +import java.util.Scanner; + +/** + * Config Parser - Part 2 + * https://www.udemy.com/course/java-reflection-master-class + */ +public class ConfigLoaderLibrary { + private static final Path GAME_CONFIG_PATH = Path.of("resources/game-properties.cfg"); + private static final Path UI_CONFIG_PATH = Path.of("resources/user-interface.cfg"); + + public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, IOException { + GameConfig config = createConfigObject(GameConfig.class, GAME_CONFIG_PATH); + //UserInterfaceConfig config = createConfigObject(UserInterfaceConfig.class, UI_CONFIG_PATH); + + System.out.println(config); + } + + public static T createConfigObject(Class clazz, Path filePath) throws IOException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { + + Scanner scanner = new Scanner(filePath); + + Constructor constructor = clazz.getDeclaredConstructor(); + constructor.setAccessible(true); + + T configInstance = (T) constructor.newInstance(); + + while (scanner.hasNextLine()) { + String configLine = scanner.nextLine(); + + String[] nameValuePair = configLine.split("="); + + if (nameValuePair.length != 2) { + continue; + } + + String propertyName = nameValuePair[0]; + String propertyValue = nameValuePair[1]; + + Field field; + try { + field = clazz.getDeclaredField(propertyName); + } catch (NoSuchFieldException e) { + System.out.println(String.format("Property name : %s is unsupported", propertyName)); + continue; + } + + field.setAccessible(true); + + Object parsedValue; + + if (field.getType().isArray()) { + parsedValue = parseArray(field.getType().getComponentType(), propertyValue); + } else { + parsedValue = parseValue(field.getType(), propertyValue); + } + + field.set(configInstance, parsedValue); + } + + return configInstance; + } + + private static Object parseArray(Class arrayElementType, String value) { + String[] elementValues = value.split(","); + + Object arrayObject = Array.newInstance(arrayElementType, elementValues.length); + + for (int i = 0; i < elementValues.length; i++) { + Array.set(arrayObject, i, parseValue(arrayElementType, elementValues[i])); + } + return arrayObject; + } + + private static Object parseValue(Class type, String value) { + if (type.equals(int.class)) { + return Integer.parseInt(value); + } else if (type.equals(short.class)) { + return Short.parseShort(value); + } else if (type.equals(long.class)) { + return Long.parseLong(value); + } else if (type.equals(double.class)) { + return Double.parseDouble(value); + } else if (type.equals(float.class)) { + return Float.parseFloat(value); + } else if (type.equals(String.class)) { + return value; + } + throw new RuntimeException(String.format("Type : %s unsupported", type.getTypeName())); + } +} diff --git a/src/main/java/reflections/configparser/data/GameConfig.java b/src/main/java/reflections/configparser/data/GameConfig.java new file mode 100644 index 0000000..706c58a --- /dev/null +++ b/src/main/java/reflections/configparser/data/GameConfig.java @@ -0,0 +1,38 @@ + + +package reflections.configparser.data; + +import java.util.Arrays; + +public class GameConfig { + private int releaseYear; + private String gameName; + private double price; + private String[] characterNames; + + public int getReleaseYear() { + return this.releaseYear; + } + + public String getGameName() { + return this.gameName; + } + + public double getPrice() { + return this.price; + } + + public String[] getCharacterNames() { + return characterNames; + } + + @Override + public String toString() { + return "GameConfig{" + + "releaseYear=" + releaseYear + + ", gameName='" + gameName + '\'' + + ", price=" + price + + ", characterName=" + Arrays.toString(characterNames) + + '}'; + } +} diff --git a/src/main/java/reflections/configparser/data/UserInterfaceConfig.java b/src/main/java/reflections/configparser/data/UserInterfaceConfig.java new file mode 100644 index 0000000..38f901f --- /dev/null +++ b/src/main/java/reflections/configparser/data/UserInterfaceConfig.java @@ -0,0 +1,32 @@ + + +package reflections.configparser.data; + +import java.util.Arrays; + +public class UserInterfaceConfig { + private String titleText; + private String[] titleFonts; + private short[] titleTextSizes; + + public String getTitleText() { + return titleText; + } + + public String[] getTitleFonts() { + return titleFonts; + } + + public short[] getTitleTextSizes() { + return titleTextSizes; + } + + @Override + public String toString() { + return "UserInterfaceConfig{" + + "titleText='" + titleText + '\'' + + ", titleFonts=" + Arrays.toString(titleFonts) + + ", titleTextSizes=" + Arrays.toString(titleTextSizes) + + '}'; + } +} diff --git a/src/main/java/reflections/configparser/resources/game-properties.cfg b/src/main/java/reflections/configparser/resources/game-properties.cfg new file mode 100644 index 0000000..d8a2b62 --- /dev/null +++ b/src/main/java/reflections/configparser/resources/game-properties.cfg @@ -0,0 +1,7 @@ +releaseYear=1993 + +gameName=The Lost Vikings + +price=10.99 + +characterNames=Erik the Swift,Baleog the Fierce,Olaf the Stout \ No newline at end of file diff --git a/src/main/java/reflections/configparser/resources/user-interface.cfg b/src/main/java/reflections/configparser/resources/user-interface.cfg new file mode 100644 index 0000000..f95256a --- /dev/null +++ b/src/main/java/reflections/configparser/resources/user-interface.cfg @@ -0,0 +1,3 @@ +titleText=Welcome to Our Store +titleFonts=Ariel,Sans-serif +titleTextSizes=10,15 \ No newline at end of file diff --git a/src/main/java/reflections/customMockito/ExternalService.java b/src/main/java/reflections/customMockito/ExternalService.java new file mode 100644 index 0000000..63381a4 --- /dev/null +++ b/src/main/java/reflections/customMockito/ExternalService.java @@ -0,0 +1,9 @@ +package reflections.customMockito; + +public interface ExternalService { + public String concat(String arg1, String arg2); + + public void someStrangeOperation(Object obj); + + public int divide(int a, int b); +} \ No newline at end of file diff --git a/src/main/java/reflections/customMockito/OurMockTest.java b/src/main/java/reflections/customMockito/OurMockTest.java new file mode 100644 index 0000000..ed35441 --- /dev/null +++ b/src/main/java/reflections/customMockito/OurMockTest.java @@ -0,0 +1,26 @@ +package reflections.customMockito; + +public class OurMockTest { + ExternalService externalService = (ExternalService) OurMockito.mock(ExternalService.class); + + //@Test + public void stubbing_method() throws Exception { + OurMockito.stub(externalService, "concat", "dummy"); + String returned = externalService.concat(null, null); + //assertEquals("dummy", returned); + } + + // @Test + public void stubbing_error_conditions() throws Exception { + OurMockito.stub(externalService, "divide", 0); + int returned = externalService.divide(0, 0); + //assertEquals(0, returned); + } + + // @Test + public void stubbing_exception() throws Exception { + OurMockito.stub(externalService, "someStrangeOperation", new + RuntimeException("Just blow this up!")); + externalService.someStrangeOperation(null); + } +} \ No newline at end of file diff --git a/src/main/java/reflections/customMockito/OurMockito.java b/src/main/java/reflections/customMockito/OurMockito.java new file mode 100644 index 0000000..bf52e9c --- /dev/null +++ b/src/main/java/reflections/customMockito/OurMockito.java @@ -0,0 +1,50 @@ +package reflections.customMockito; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Proxy; +import java.util.HashMap; +import java.util.Map; + +public class OurMockito implements InvocationHandler { + private static Map stubMap = new HashMap<>(); + private static Map excepMap = new + HashMap<>(); + + @Override + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable { + String methodName = method.getName(); + if (Modifier.isFinal(method.getModifiers()) || + Modifier.isPrivate(method.getModifiers()) || + Modifier.isStatic(method.getModifiers())) { + throw new RuntimeException("You naughty developer mocking a private,static or final method " + methodName); + } + if (excepMap.containsKey(methodName)) { + Exception excep = excepMap.get(methodName); + throw excep; + } + if (stubMap.containsKey(methodName)) { + return stubMap.get(methodName); + } + return null; + } + + public static Object mock(Class aClass) { + return Proxy.newProxyInstance + (OurMockito.class.getClassLoader(), new Class[]{aClass + }, new OurMockito()); + } + + public static void stub(Object stubOn, String methodName, Object stubbedValue) { + stubMap.put(methodName, stubbedValue); + } + + public static void stub(Object stubOn, String methodName, + Exception excep) { + if (excep != null) { + excepMap.put(methodName, excep); + } + } +} \ No newline at end of file diff --git a/src/main/java/reflections/dynamicProxy/Main.java b/src/main/java/reflections/dynamicProxy/Main.java new file mode 100644 index 0000000..5ae3a0a --- /dev/null +++ b/src/main/java/reflections/dynamicProxy/Main.java @@ -0,0 +1,92 @@ +package reflections.dynamicProxy; + +import reflections.dynamicProxy.external.impl.DatabaseReader; +import reflections.dynamicProxy.external.impl.HttpClient; +import reflections.dynamicProxy.external.impl.DatabaseReaderImpl; + +import java.io.IOException; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.List; + +/** + * Dynamic Proxy + * https://www.udemy.com/course/java-reflection-master-class + */ +public class Main { + + public static void main(String[] args) throws InterruptedException { + DatabaseReader databaseReader = createProxy(new DatabaseReaderImpl()); + + useDatabaseReader(databaseReader); + + List listOfGreetings = new ArrayList<>(); + + listOfGreetings.add("hello"); + listOfGreetings.add("good morning"); + listOfGreetings.remove("hello"); + } + + public static void useHttpClient(HttpClient httpClient) { + httpClient.initialize(); + String response = httpClient.sendRequest("some request"); + + System.out.println(String.format("Http response is : %s", response)); + } + + public static void useDatabaseReader(DatabaseReader databaseReader) throws InterruptedException { + int rowsInGamesTable = 0; + try { + rowsInGamesTable = databaseReader.countRowsInTable("GamesTable"); + } catch (IOException e) { + System.out.println("Catching exception " + e); + return; + } + + System.out.println(String.format("There are %s rows in GamesTable", rowsInGamesTable)); + + String[] data = databaseReader.readRow("SELECT * from GamesTable"); + + System.out.println(String.format("Received %s", String.join(" , ", data))); + } + + @SuppressWarnings("unchecked") + public static T createProxy(Object originalObject) { + Class[] interfaces = originalObject.getClass().getInterfaces(); + + TimeMeasuringProxyHandler timeMeasuringProxyHandler = new TimeMeasuringProxyHandler(originalObject); + + return (T) Proxy.newProxyInstance(originalObject.getClass().getClassLoader(), interfaces, timeMeasuringProxyHandler); + } + + public static class TimeMeasuringProxyHandler implements InvocationHandler { + private final Object originalObject; + + public TimeMeasuringProxyHandler(Object originalObject) { + this.originalObject = originalObject; + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + Object result; + + System.out.println(String.format("Measuring Proxy - Before Executing method : %s()", method.getName())); + + long startTime = System.nanoTime(); + try { + result = method.invoke(originalObject, args); + } catch (InvocationTargetException e) { + throw e.getTargetException(); + } + long endTime = System.nanoTime(); + + System.out.println(); + System.out.println(String.format("Measuring Proxy - Execution of %s() took %dns \n", method.getName(), endTime - startTime)); + + return result; + } + } +} diff --git a/src/main/java/reflections/dynamicProxy/external/impl/DatabaseReader.java b/src/main/java/reflections/dynamicProxy/external/impl/DatabaseReader.java new file mode 100644 index 0000000..b3673a5 --- /dev/null +++ b/src/main/java/reflections/dynamicProxy/external/impl/DatabaseReader.java @@ -0,0 +1,12 @@ + + +package reflections.dynamicProxy.external.impl; + +import java.io.IOException; + +public interface DatabaseReader { + + int countRowsInTable(String tableName) throws InterruptedException, IOException; + + String[] readRow(String sqlQuery) throws InterruptedException; +} diff --git a/src/main/java/reflections/dynamicProxy/external/impl/DatabaseReaderImpl.java b/src/main/java/reflections/dynamicProxy/external/impl/DatabaseReaderImpl.java new file mode 100644 index 0000000..a5c484c --- /dev/null +++ b/src/main/java/reflections/dynamicProxy/external/impl/DatabaseReaderImpl.java @@ -0,0 +1,22 @@ + + +package reflections.dynamicProxy.external.impl; + + +public final class DatabaseReaderImpl implements DatabaseReader { + @Override + public int countRowsInTable(String tableName) throws InterruptedException { + System.out.println(String.format("DatabaseReaderImpl - counting rows in table %s", tableName)); + + Thread.sleep(1000); + return 50; + } + + @Override + public String[] readRow(String sqlQuery) throws InterruptedException { + System.out.println(String.format("DatabaseReaderImpl - Executing SQL query : %s", sqlQuery)); + + Thread.sleep(1500); + return new String[]{"column1", "column2", "column3"}; + } +} diff --git a/src/main/java/reflections/dynamicProxy/external/impl/HttpClient.java b/src/main/java/reflections/dynamicProxy/external/impl/HttpClient.java new file mode 100644 index 0000000..73c005b --- /dev/null +++ b/src/main/java/reflections/dynamicProxy/external/impl/HttpClient.java @@ -0,0 +1,10 @@ + + +package reflections.dynamicProxy.external.impl; + +public interface HttpClient { + + void initialize(); + + String sendRequest(String request); +} diff --git a/src/main/java/reflections/dynamicProxy/external/impl/HttpClientImpl.java b/src/main/java/reflections/dynamicProxy/external/impl/HttpClientImpl.java new file mode 100644 index 0000000..86fb9d7 --- /dev/null +++ b/src/main/java/reflections/dynamicProxy/external/impl/HttpClientImpl.java @@ -0,0 +1,27 @@ + + +package reflections.dynamicProxy.external.impl; + +public final class HttpClientImpl implements HttpClient { + @Override + public void initialize() { + System.out.println("Initializing HTTP client"); + try { + Thread.sleep(500); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + + @Override + public String sendRequest(String request) { + System.out.println(String.format("Sending request %s", request)); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println("Received response"); + return "someResponse data"; + } +} diff --git a/src/main/java/reflections/game/Game.java b/src/main/java/reflections/game/Game.java new file mode 100644 index 0000000..ab3ab5d --- /dev/null +++ b/src/main/java/reflections/game/Game.java @@ -0,0 +1,8 @@ + + +package reflections.game; + +public interface Game { + + void startGame(); +} diff --git a/src/main/java/reflections/game/internal/Board.java b/src/main/java/reflections/game/internal/Board.java new file mode 100644 index 0000000..37ec6ed --- /dev/null +++ b/src/main/java/reflections/game/internal/Board.java @@ -0,0 +1,123 @@ + + +package reflections.game.internal; + +class Board { + private Cell[][] cells; + private BoardDimensions dimensions; + + public Board(BoardDimensions boardDimensions) { + this.dimensions = boardDimensions; + this.cells = new Cell[boardDimensions.getNumberOfColumns()][boardDimensions.getNumberOfRows()]; + initAllCells(); + } + + private void initAllCells() { + for (int r = 0; r < dimensions.getNumberOfRows(); r++) { + for (int c = 0; c < dimensions.getNumberOfColumns(); c++) { + this.cells[c][r] = new Cell(); + } + } + } + + public void updateCell(int row, int column, Sign sign) { + this.cells[column][row].setSign(sign); + } + + public Sign checkWinner() { + // Check rows + for (int r = 0; r < dimensions.getNumberOfRows(); r++) { + Sign sign = getRowWinner(r); + if (sign != Sign.EMPTY) { + return sign; + } + } + + // Check columns + for (int c = 0; c < dimensions.getNumberOfColumns(); c++) { + Sign sign = getColumnWinner(c); + if (sign != Sign.EMPTY) { + return sign; + } + } + + // Check diagonal + Sign sign = getDiagonalWinner(0, 0, 1, 1); + if (sign != Sign.EMPTY) { + return sign; + } + + // Check diagonal + return getDiagonalWinner(0, dimensions.getNumberOfColumns() - 1, -1, 1); + } + + public boolean isCellEmpty(int row, int column) { + return this.cells[column][row].isEmpty(); + } + + public char getPrintableCellSign(int row, int column) { + return this.cells[column][row].getSign().getValue(); + } + + public boolean isBoardFull() { + for (int r = 0; r < dimensions.getNumberOfRows(); r++) { + for (int c = 0; c < dimensions.getNumberOfColumns(); c++) { + if (this.cells[c][r].isEmpty()) { + return false; + } + } + } + return true; + } + + private Sign getColumnWinner(int currentColumn) { + Sign initialSign = this.cells[currentColumn][0].getSign(); + + if (initialSign == Sign.EMPTY) { + return initialSign; + } + + for (int r = 1; r < dimensions.getNumberOfRows(); r++) { + if (this.cells[currentColumn][r].getSign() != initialSign) { + return Sign.EMPTY; + } + } + return initialSign; + } + + private Sign getRowWinner(int currentRow) { + Sign initialSign = this.cells[0][currentRow].getSign(); + + if (initialSign == Sign.EMPTY) { + return initialSign; + } + + for (int c = 1; c < dimensions.getNumberOfColumns(); c++) { + if (this.cells[c][currentRow].getSign() != initialSign) { + return Sign.EMPTY; + } + } + return initialSign; + } + + + private Sign getDiagonalWinner(int startRow, int startColumn, int horizontalStep, int verticalStep) { + Sign initialSign = this.cells[startColumn][startRow].getSign(); + if (initialSign == Sign.EMPTY) { + return Sign.EMPTY; + } + + int r = startRow + verticalStep; + int c = startColumn + horizontalStep; + + while (r < dimensions.getNumberOfRows() && c < dimensions.getNumberOfColumns()) { + if (this.cells[c][r].getSign() != initialSign) { + return Sign.EMPTY; + } + r += verticalStep; + c += horizontalStep; + } + + return initialSign; + } +} diff --git a/src/main/java/reflections/game/internal/BoardDimensions.java b/src/main/java/reflections/game/internal/BoardDimensions.java new file mode 100644 index 0000000..634ecb4 --- /dev/null +++ b/src/main/java/reflections/game/internal/BoardDimensions.java @@ -0,0 +1,17 @@ + + +package reflections.game.internal; + +class BoardDimensions { + private static final int NUM_OF_ROWS = 3; + private static final int NUM_OF_COLUMNS = 3; + + + public int getNumberOfRows() { + return NUM_OF_ROWS; + } + + public int getNumberOfColumns() { + return NUM_OF_COLUMNS; + } +} diff --git a/src/main/java/reflections/game/internal/BoardLocation.java b/src/main/java/reflections/game/internal/BoardLocation.java new file mode 100644 index 0000000..ae8d6ac --- /dev/null +++ b/src/main/java/reflections/game/internal/BoardLocation.java @@ -0,0 +1,21 @@ + + +package reflections.game.internal; + +class BoardLocation { + private int row; + private int column; + + public BoardLocation(int row, int column) { + this.row = row; + this.column = column; + } + + public int getRow() { + return row; + } + + public int getColumn() { + return column; + } +} diff --git a/src/main/java/reflections/game/internal/BoardPrinter.java b/src/main/java/reflections/game/internal/BoardPrinter.java new file mode 100644 index 0000000..863d5b6 --- /dev/null +++ b/src/main/java/reflections/game/internal/BoardPrinter.java @@ -0,0 +1,36 @@ + + +package reflections.game.internal; + +class BoardPrinter { + private final BoardDimensions dimensions; + + public BoardPrinter(BoardDimensions dimensions) { + this.dimensions = dimensions; + } + + public void print(Board board) { + printHorizontalBorder(); + + printBoard(board); + + printHorizontalBorder(); + } + + private void printBoard(Board board) { + for (int r = 0; r < dimensions.getNumberOfRows(); r++) { + System.out.print("|"); + for (int c = 0; c < dimensions.getNumberOfColumns(); c++) { + System.out.print(String.format(" %s |", board.getPrintableCellSign(r, c))); + } + System.out.println(); + } + } + + private void printHorizontalBorder() { + for (int c = 0; c < dimensions.getNumberOfColumns() * 4 + 1; c++) { + System.out.print("-"); + } + System.out.println(); + } +} diff --git a/src/main/java/reflections/game/internal/Cell.java b/src/main/java/reflections/game/internal/Cell.java new file mode 100644 index 0000000..12bae44 --- /dev/null +++ b/src/main/java/reflections/game/internal/Cell.java @@ -0,0 +1,23 @@ + + +package reflections.game.internal; + +class Cell { + private Sign sign; + + public Cell() { + sign = Sign.EMPTY; + } + + public boolean isEmpty() { + return sign == Sign.EMPTY; + } + + public Sign getSign() { + return sign; + } + + public void setSign(Sign sign) { + this.sign = sign; + } +} diff --git a/src/main/java/reflections/game/internal/ComputerInputProvider.java b/src/main/java/reflections/game/internal/ComputerInputProvider.java new file mode 100644 index 0000000..7c9baa4 --- /dev/null +++ b/src/main/java/reflections/game/internal/ComputerInputProvider.java @@ -0,0 +1,33 @@ + + +package reflections.game.internal; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +class ComputerInputProvider implements InputProvider { + public final BoardDimensions dimensions; + private final Random random = new Random(); + + public ComputerInputProvider(BoardDimensions dimensions) { + this.dimensions = dimensions; + } + + @Override + public BoardLocation provideNextMove(Board board) { + List availableCells = new ArrayList<>(); + + for (int r = 0; r < dimensions.getNumberOfRows(); r++) { + for (int c = 0; c < dimensions.getNumberOfColumns(); c++) { + if (board.isCellEmpty(r, c)) { + availableCells.add(new BoardLocation(r, c)); + } + } + } + + int chosenCell = random.nextInt(availableCells.size()); + + return availableCells.get(chosenCell); + } +} diff --git a/src/main/java/reflections/game/internal/ComputerPlayer.java b/src/main/java/reflections/game/internal/ComputerPlayer.java new file mode 100644 index 0000000..724482e --- /dev/null +++ b/src/main/java/reflections/game/internal/ComputerPlayer.java @@ -0,0 +1,23 @@ + + +package reflections.game.internal; + +class ComputerPlayer implements Player { + private static final String NAME = "Computer"; + private final ComputerInputProvider locationProvider; + + public ComputerPlayer(ComputerInputProvider locationProvider) { + this.locationProvider = locationProvider; + } + + @Override + public void play(Board board, Sign sign) { + BoardLocation location = locationProvider.provideNextMove(board); + board.updateCell(location.getRow(), location.getColumn(), sign); + } + + @Override + public String getPlayerName() { + return NAME; + } +} diff --git a/src/main/java/reflections/game/internal/HumanPlayer.java b/src/main/java/reflections/game/internal/HumanPlayer.java new file mode 100644 index 0000000..a7c7cb5 --- /dev/null +++ b/src/main/java/reflections/game/internal/HumanPlayer.java @@ -0,0 +1,23 @@ + + +package reflections.game.internal; + +class HumanPlayer implements Player { + private static final String NAME = "You"; + private final KeyboardInputProvider inputProvider; + + public HumanPlayer(KeyboardInputProvider inputProvider) { + this.inputProvider = inputProvider; + } + + @Override + public void play(Board board, Sign sign) { + BoardLocation nextMoveLocation = inputProvider.provideNextMove(board); + board.updateCell(nextMoveLocation.getRow(), nextMoveLocation.getColumn(), sign); + } + + @Override + public String getPlayerName() { + return NAME; + } +} diff --git a/src/main/java/reflections/game/internal/InputProvider.java b/src/main/java/reflections/game/internal/InputProvider.java new file mode 100644 index 0000000..7d39e3c --- /dev/null +++ b/src/main/java/reflections/game/internal/InputProvider.java @@ -0,0 +1,7 @@ + + +package reflections.game.internal; + +interface InputProvider { + BoardLocation provideNextMove(Board board); +} diff --git a/src/main/java/reflections/game/internal/KeyboardInputProvider.java b/src/main/java/reflections/game/internal/KeyboardInputProvider.java new file mode 100644 index 0000000..5cdd1d0 --- /dev/null +++ b/src/main/java/reflections/game/internal/KeyboardInputProvider.java @@ -0,0 +1,31 @@ + + +package reflections.game.internal; + +import java.util.Scanner; + +class KeyboardInputProvider implements InputProvider { + private final Scanner scanner = new Scanner(System.in); + private final BoardDimensions boardDimensions; + + public KeyboardInputProvider(BoardDimensions boardDimensions) { + this.boardDimensions = boardDimensions; + } + + @Override + public BoardLocation provideNextMove(Board board) { + int row; + int column; + do { + System.out.print("Please choose row: "); + row = scanner.nextInt(); + System.out.print("Please choose column: "); + column = scanner.nextInt(); + } while (row < 0 + || row >= boardDimensions.getNumberOfRows() + || column < 0 + || column >= boardDimensions.getNumberOfColumns() + || !board.isCellEmpty(row, column)); + return new BoardLocation(row, column); + } +} diff --git a/src/main/java/reflections/game/internal/Player.java b/src/main/java/reflections/game/internal/Player.java new file mode 100644 index 0000000..4e7ab4a --- /dev/null +++ b/src/main/java/reflections/game/internal/Player.java @@ -0,0 +1,10 @@ + + +package reflections.game.internal; + +interface Player { + + void play(Board board, Sign sign); + + String getPlayerName(); +} diff --git a/src/main/java/reflections/game/internal/Sign.java b/src/main/java/reflections/game/internal/Sign.java new file mode 100644 index 0000000..2fac9c1 --- /dev/null +++ b/src/main/java/reflections/game/internal/Sign.java @@ -0,0 +1,19 @@ + + +package reflections.game.internal; + +enum Sign { + EMPTY(' '), + X('X'), + Y('Y'); + + private char value; + + Sign(char value) { + this.value = value; + } + + public char getValue() { + return this.value; + } +} diff --git a/src/main/java/reflections/game/internal/TicTacToeGame.java b/src/main/java/reflections/game/internal/TicTacToeGame.java new file mode 100644 index 0000000..bf725e3 --- /dev/null +++ b/src/main/java/reflections/game/internal/TicTacToeGame.java @@ -0,0 +1,57 @@ + + +package reflections.game.internal; + + +import reflections.game.Game; + +import java.util.Arrays; +import java.util.List; +import java.util.Random; + +public class TicTacToeGame implements Game { + private static final int NUMBER_OF_PLAYERS = 2; + private final Random random = new Random(); + private final List playerSigns = Arrays.asList(Sign.X, Sign.Y); + + private final Board board; + private final List players; + private final BoardPrinter printer; + + TicTacToeGame(Board board, + BoardPrinter printer, + HumanPlayer humanPlayer, + ComputerPlayer computerPlayer) { + this.board = board; + this.printer = printer; + this.players = Arrays.asList(humanPlayer, computerPlayer); + } + + public void startGame() { + Sign winner; + int nextPlayerIndex = random.nextInt(NUMBER_OF_PLAYERS); + + Player player; + printer.print(board); + do { + nextPlayerIndex = NUMBER_OF_PLAYERS - nextPlayerIndex - 1; + + player = players.get(nextPlayerIndex); + + Sign playerSign = playerSigns.get(nextPlayerIndex); + + player.play(board, playerSign); + + winner = board.checkWinner(); + + printer.print(board); + } while (!board.isBoardFull() && winner == Sign.EMPTY); + + if (winner != Sign.EMPTY) { + System.out.println(String.format("Winner is : %s", player.getPlayerName())); + } else { + System.out.println("Game over! Nobody won."); + } + + } +} diff --git a/src/main/java/reflections/init/Main.java b/src/main/java/reflections/init/Main.java new file mode 100644 index 0000000..6d1d71d --- /dev/null +++ b/src/main/java/reflections/init/Main.java @@ -0,0 +1,40 @@ + +package reflections.init; +import reflections.game.Game; +import reflections.game.internal.TicTacToeGame; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; + +public class Main { + + public static void main(String[] args) throws IllegalAccessException, InstantiationException, InvocationTargetException { + Game game = (Game) createObjectRecursively(TicTacToeGame.class); + game.startGame(); + } + + public static T createObjectRecursively(Class clazz) throws IllegalAccessException, InvocationTargetException, InstantiationException { + Constructor constructor = getFirstConstructor(clazz); + + List constructorArguments = new ArrayList<>(); + + for (Class argumentType : constructor.getParameterTypes()) { + Object argumentValue = createObjectRecursively(argumentType); + constructorArguments.add(argumentValue); + } + + constructor.setAccessible(true); + return (T) constructor.newInstance(constructorArguments.toArray()); + } + + private static Constructor getFirstConstructor(Class clazz) { + Constructor[] constructors = clazz.getDeclaredConstructors(); + if (constructors.length == 0) { + throw new IllegalStateException(String.format("No constructor has been found for class %s", clazz.getName())); + } + + return constructors[0]; + } +} diff --git a/src/main/java/reflections/methodDiscovery/Address.java b/src/main/java/reflections/methodDiscovery/Address.java new file mode 100644 index 0000000..d354f31 --- /dev/null +++ b/src/main/java/reflections/methodDiscovery/Address.java @@ -0,0 +1,19 @@ + +package reflections.methodDiscovery; + +public class Address { + private String city; + private String state; + + public String getCity() { + return city; + } + + public void setCity(String city) { + this.city = city; + } + + public String getState() { + return state; + } +} diff --git a/src/main/java/reflections/methodDiscovery/ClothingProduct.java b/src/main/java/reflections/methodDiscovery/ClothingProduct.java new file mode 100644 index 0000000..a43e4b5 --- /dev/null +++ b/src/main/java/reflections/methodDiscovery/ClothingProduct.java @@ -0,0 +1,26 @@ + +package reflections.methodDiscovery; + +public class ClothingProduct extends Product { + private Size size; + private String color; + + // Getters + public Size getSize() { + return size; + } + + public void setSize(Size size) { + this.size = size; + } + + // Setters + + public String getColor() { + return color; + } + + public void setColor(String color) { + this.color = color; + } +} diff --git a/src/main/java/reflections/methodDiscovery/Product.java b/src/main/java/reflections/methodDiscovery/Product.java new file mode 100644 index 0000000..ee3aca1 --- /dev/null +++ b/src/main/java/reflections/methodDiscovery/Product.java @@ -0,0 +1,54 @@ + +package reflections.methodDiscovery; + +import java.util.Date; + +public class Product { + private double price; + private String name; + private long quantity; + private Date expirationDate; + private Address address; + + // Getters + public double getPrice() { + return price; + } + + // Setters + public void setPrice(double price) { + this.price = price; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public long getQuantity() { + return quantity; + } + + public void setQuantity(long quantity) { + this.quantity = quantity; + } + + public Date getExpirationDate() { + return expirationDate; + } + + public void setExpirationDate(Date expirationDate) { + this.expirationDate = expirationDate; + } + + public Address getAddress() { + return address; + } + + public void setAddress(Address address) { + this.address = address; + } +} diff --git a/src/main/java/reflections/methodDiscovery/ProductTest.java b/src/main/java/reflections/methodDiscovery/ProductTest.java new file mode 100644 index 0000000..5a4a310 --- /dev/null +++ b/src/main/java/reflections/methodDiscovery/ProductTest.java @@ -0,0 +1,94 @@ + +package reflections.methodDiscovery; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.*; + +public class ProductTest { + + public static void main(String[] args) { + testGetters(ClothingProduct.class); + testSetters(ClothingProduct.class); + } + + public static void testSetters(Class dataClass) { + List fields = getAllFields(dataClass); + + for (Field field : fields) { + String setterName = "set" + capitalizeFirstLetter(field.getName()); + + Method setterMethod = null; + try { + setterMethod = dataClass.getMethod(setterName, field.getType()); + } catch (NoSuchMethodException e) { + throw new IllegalStateException(String.format("Setter : %s not found", setterName)); + } + + if (!setterMethod.getReturnType().equals(void.class)) { + throw new IllegalStateException(String.format("Setter method : %s has to return void", setterName)); + } + } + } + + public static void testGetters(Class dataClass) { + List fields = getAllFields(dataClass); + + Map methodNameToMethod = mapMethodNameToMethod(dataClass); + + for (Field field : fields) { + String getterName = "get" + capitalizeFirstLetter(field.getName()); + + if (!methodNameToMethod.containsKey(getterName)) { + throw new IllegalStateException(String.format("Field : %s doesn't have a getter method", field.getName())); + } + + Method getter = methodNameToMethod.get(getterName); + + if (!getter.getReturnType().equals(field.getType())) { + throw new IllegalStateException( + String.format("Getter method : %s() has return type %s but expected %s", + getter.getName(), + getter.getReturnType().getTypeName(), + field.getType().getTypeName())); + } + + if (getter.getParameterCount() > 0) { + throw new IllegalStateException(String.format("Getter : %s has %d arguments", getterName)); + } + } + } + + private static List getAllFields(Class clazz) { + if (clazz == null || clazz.equals(Object.class)) { + return Collections.emptyList(); + } + + Field[] currentClassFields = clazz.getDeclaredFields(); + + List inheritedFields = getAllFields(clazz.getSuperclass()); + + List allFields = new ArrayList<>(); + + allFields.addAll(Arrays.asList(currentClassFields)); + allFields.addAll(inheritedFields); + + return allFields; + } + + private static String capitalizeFirstLetter(String fieldName) { + return fieldName.substring(0, 1).toUpperCase().concat(fieldName.substring(1)); + } + + private static Map mapMethodNameToMethod(Class dataClass) { + Method[] allMethods = dataClass.getMethods(); + + Map nameToMethod = new HashMap<>(); + + for (Method method : allMethods) { + nameToMethod.put(method.getName(), method); + } + + return nameToMethod; + } +} diff --git a/src/main/java/reflections/methodDiscovery/Size.java b/src/main/java/reflections/methodDiscovery/Size.java new file mode 100644 index 0000000..3a82752 --- /dev/null +++ b/src/main/java/reflections/methodDiscovery/Size.java @@ -0,0 +1,9 @@ + +package reflections.methodDiscovery; + +public enum Size { + SMALL, + MEDIUM, + LARGE, + XLARGE +} diff --git a/src/main/java/reflections/retries/Main.java b/src/main/java/reflections/retries/Main.java new file mode 100644 index 0000000..954b6d8 --- /dev/null +++ b/src/main/java/reflections/retries/Main.java @@ -0,0 +1,140 @@ +package reflections.retries; + +import reflections.retries.annotations.InitializerClass; +import reflections.retries.annotations.InitializerMethod; +import reflections.retries.annotations.RetryOperation; +import reflections.retries.annotations.ScanPackages; + +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Annotations - Retry + * https://www.udemy.com/course/java-reflection-master-class + */ + +@ScanPackages({"app", "app.configs", "app.databases", "app.http"}) +public class Main { + + public static void main(String[] args) throws Throwable { + initialize(); + } + + public static void initialize() throws Throwable { + ScanPackages scanPackages = Main.class.getAnnotation(ScanPackages.class); + + if (scanPackages == null || scanPackages.value().length == 0) { + return; + } + + List> classes = getAllClasses(scanPackages.value()); + + for (Class clazz : classes) { + if (!clazz.isAnnotationPresent(InitializerClass.class)) { + continue; + } + + List methods = getAllInitializingMethods(clazz); + + Object instance = clazz.getDeclaredConstructor().newInstance(); + + for (Method method : methods) { + callInitializingMethod(instance, method); + } + } + } + + private static void callInitializingMethod(Object instance, Method method) throws Throwable { + RetryOperation retryOperation = method.getAnnotation(RetryOperation.class); + + int numberOfRetries = retryOperation == null ? 0 : retryOperation.numberOfRetries(); + + while (true) { + try { + method.invoke(instance); + break; + } catch (InvocationTargetException e) { + Throwable targetException = e.getTargetException(); + + if (numberOfRetries > 0 && Set.of(retryOperation.retryExceptions()).contains(targetException.getClass())) { + numberOfRetries--; + + System.out.println("Retrying ..."); + Thread.sleep(retryOperation.durationBetweenRetriesMs()); + } else if (retryOperation != null) { + throw new Exception(retryOperation.failureMessage(), targetException); + } else { + throw targetException; + } + } + } + } + + private static List getAllInitializingMethods(Class clazz) { + List initializingMethods = new ArrayList<>(); + for (Method method : clazz.getDeclaredMethods()) { + if (method.isAnnotationPresent(InitializerMethod.class)) { + initializingMethods.add(method); + } + } + return initializingMethods; + } + + public static List> getAllClasses(String... packageNames) throws URISyntaxException, IOException, ClassNotFoundException { + List> allClasses = new ArrayList<>(); + + for (String packageName : packageNames) { + String packageRelativePath = packageName.replace('.', '/'); + + URI packageUri = Main.class.getResource(packageRelativePath).toURI(); + + if (packageUri.getScheme().equals("file")) { + Path packageFullPath = Paths.get(packageUri); + allClasses.addAll(getAllPackageClasses(packageFullPath, packageName)); + } else if (packageUri.getScheme().equals("jar")) { + FileSystem fileSystem = FileSystems.newFileSystem(packageUri, Collections.emptyMap()); + + Path packageFullPathInJar = fileSystem.getPath(packageRelativePath); + allClasses.addAll(getAllPackageClasses(packageFullPathInJar, packageName)); + + fileSystem.close(); + } + } + return allClasses; + } + + private static List> getAllPackageClasses(Path packagePath, String packageName) throws IOException, ClassNotFoundException { + + if (!Files.exists(packagePath)) { + return Collections.emptyList(); + } + + List files = Files.list(packagePath) + .filter(Files::isRegularFile) + .collect(Collectors.toList()); + + List> classes = new ArrayList<>(); + + for (Path filePath : files) { + String fileName = filePath.getFileName().toString(); + + if (fileName.endsWith(".class")) { + String classFullName = packageName.isBlank() ? + fileName.replaceFirst("\\.class$", "") + : packageName + "." + fileName.replaceFirst("\\.class$", ""); + Class clazz = Class.forName(classFullName); + classes.add(clazz); + } + } + return classes; + } +} diff --git a/src/main/java/reflections/retries/annotations/InitializerClass.java b/src/main/java/reflections/retries/annotations/InitializerClass.java new file mode 100644 index 0000000..4d01471 --- /dev/null +++ b/src/main/java/reflections/retries/annotations/InitializerClass.java @@ -0,0 +1,13 @@ + + +package reflections.retries.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface InitializerClass { +} diff --git a/src/main/java/reflections/retries/annotations/InitializerMethod.java b/src/main/java/reflections/retries/annotations/InitializerMethod.java new file mode 100644 index 0000000..918d98f --- /dev/null +++ b/src/main/java/reflections/retries/annotations/InitializerMethod.java @@ -0,0 +1,13 @@ + + +package reflections.retries.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface InitializerMethod { +} diff --git a/src/main/java/reflections/retries/annotations/RetryOperation.java b/src/main/java/reflections/retries/annotations/RetryOperation.java new file mode 100644 index 0000000..bc9c3f7 --- /dev/null +++ b/src/main/java/reflections/retries/annotations/RetryOperation.java @@ -0,0 +1,20 @@ + + +package reflections.retries.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface RetryOperation { + Class[] retryExceptions() default {Exception.class}; + + long durationBetweenRetriesMs() default 0; + + String failureMessage() default "Operation failed after retrying"; + + int numberOfRetries(); +} diff --git a/src/main/java/reflections/retries/annotations/ScanPackages.java b/src/main/java/reflections/retries/annotations/ScanPackages.java new file mode 100644 index 0000000..1bf32a7 --- /dev/null +++ b/src/main/java/reflections/retries/annotations/ScanPackages.java @@ -0,0 +1,14 @@ + + +package reflections.retries.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface ScanPackages { + String[] value(); +} diff --git a/src/main/java/reflections/retries/app/AutoSaver.java b/src/main/java/reflections/retries/app/AutoSaver.java new file mode 100644 index 0000000..9b8b278 --- /dev/null +++ b/src/main/java/reflections/retries/app/AutoSaver.java @@ -0,0 +1,15 @@ + + +package reflections.retries.app; + +import reflections.retries.annotations.InitializerClass; +import reflections.retries.annotations.InitializerMethod; + +@InitializerClass +public class AutoSaver { + + @InitializerMethod + public void startAutoSavingThreads() { + System.out.println("Start automatic data saving to disk"); + } +} diff --git a/src/main/java/reflections/retries/app/configs/ConfigsLoader.java b/src/main/java/reflections/retries/app/configs/ConfigsLoader.java new file mode 100644 index 0000000..dfe2bba --- /dev/null +++ b/src/main/java/reflections/retries/app/configs/ConfigsLoader.java @@ -0,0 +1,15 @@ + + +package reflections.retries.app.configs; + +import reflections.retries.annotations.InitializerClass; +import reflections.retries.annotations.InitializerMethod; + +@InitializerClass +public class ConfigsLoader { + + @InitializerMethod + public void loadAllConfigs() { + System.out.println("Loading all configuration files"); + } +} diff --git a/src/main/java/reflections/retries/app/databases/CacheLoader.java b/src/main/java/reflections/retries/app/databases/CacheLoader.java new file mode 100644 index 0000000..8521e05 --- /dev/null +++ b/src/main/java/reflections/retries/app/databases/CacheLoader.java @@ -0,0 +1,19 @@ + + +package reflections.retries.app.databases; + +import reflections.retries.annotations.InitializerClass; +import reflections.retries.annotations.InitializerMethod; + +@InitializerClass +public class CacheLoader { + + @InitializerMethod + public void loadCache() { + System.out.println("Loading data from cache"); + } + + public void reloadCache() { + System.out.println("Reload cache"); + } +} diff --git a/src/main/java/reflections/retries/app/databases/DatabaseConnection.java b/src/main/java/reflections/retries/app/databases/DatabaseConnection.java new file mode 100644 index 0000000..1fb91c9 --- /dev/null +++ b/src/main/java/reflections/retries/app/databases/DatabaseConnection.java @@ -0,0 +1,36 @@ + + +package reflections.retries.app.databases; + +import reflections.retries.annotations.InitializerClass; +import reflections.retries.annotations.InitializerMethod; +import reflections.retries.annotations.RetryOperation; + +import java.io.IOException; + +@InitializerClass +public class DatabaseConnection { + private int failCounter = 5; + + @RetryOperation( + numberOfRetries = 10, + retryExceptions = IOException.class, + durationBetweenRetriesMs = 1000, + failureMessage = "Connection to database 1 failed after retries" + ) + @InitializerMethod + public void connectToDatabase1() throws IOException { + System.out.println("Connecting to database 1"); + if (failCounter > 0) { + failCounter--; + throw new IOException("Connection failed"); + } + + System.out.println("Connection to database 1 succeeded"); + } + + @InitializerMethod + public void connectToDatabase2() { + System.out.println("Connecting to database 2"); + } +} diff --git a/src/main/java/reflections/retries/app/http/ServiceRegistry.java b/src/main/java/reflections/retries/app/http/ServiceRegistry.java new file mode 100644 index 0000000..0839c9f --- /dev/null +++ b/src/main/java/reflections/retries/app/http/ServiceRegistry.java @@ -0,0 +1,15 @@ + + +package reflections.retries.app.http; + +import reflections.retries.annotations.InitializerClass; +import reflections.retries.annotations.InitializerMethod; + +@InitializerClass +public class ServiceRegistry { + + @InitializerMethod + public void registerService() { + System.out.println("Service successfully registered"); + } +} diff --git a/src/main/java/reflections/topologicalsort/BestGamesFinder.java b/src/main/java/reflections/topologicalsort/BestGamesFinder.java new file mode 100644 index 0000000..b38c84b --- /dev/null +++ b/src/main/java/reflections/topologicalsort/BestGamesFinder.java @@ -0,0 +1,44 @@ +package reflections.topologicalsort; + +import reflections.topologicalsort.databases.Database; + +import java.util.*; + +import static reflections.topologicalsort.annotations.Annotations.*; + +public class BestGamesFinder { + private Database database = new Database(); + + @Operation("All-Games") + public Set getAllGames() { + return database.readAllGames(); + } + + @Operation("Game-To-Price") + public Map getGameToPrice(@DependsOn("All-Games") Set allGames) { + return database.readGameToPrice(allGames); + } + + @Operation("Game-To-Rating") + public Map getGameToRating(@DependsOn("All-Games") Set allGames) { + return database.readGameToRatings(allGames); + } + + @Operation("Score-To-Game") + public SortedMap scoreGames(@DependsOn("Game-To-Price") Map gameToPrice, + @DependsOn("Game-To-Rating") Map gameToRating) { + SortedMap gameToScore = new TreeMap<>(Collections.reverseOrder()); + for (String gameName : gameToPrice.keySet()) { + double score = (double) gameToRating.get(gameName) / gameToPrice.get(gameName); + gameToScore.put(score, gameName); + } + + return gameToScore; + } + + @FinalResult + public List getTopGames(@DependsOn("Score-To-Game") SortedMap gameToScore) { + return new ArrayList<>(gameToScore.values()); + } +} + diff --git a/src/main/java/reflections/topologicalsort/Main.java b/src/main/java/reflections/topologicalsort/Main.java new file mode 100644 index 0000000..f4ce11e --- /dev/null +++ b/src/main/java/reflections/topologicalsort/Main.java @@ -0,0 +1,105 @@ +package reflections.topologicalsort; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Parameter; +import java.util.*; + +import static reflections.topologicalsort.annotations.Annotations.*; + +/** + * Graph Execution with Inputs + * https://www.udemy.com/course/java-reflection-master-class + */ +public class Main { + + public static void main(String[] args) throws InvocationTargetException, IllegalAccessException { + SqlQueryBuilder sqlQueryBuilder = new SqlQueryBuilder(Arrays.asList("1", "2", "3"), + 10, + "Movies", + Arrays.asList("Id", "Name")); + + String sqlQuery = execute(sqlQueryBuilder); + System.out.println(sqlQuery); + } + + public static T execute(Object instance) throws InvocationTargetException, IllegalAccessException { + Class clazz = instance.getClass(); + + Map operationToMethod = getOperationToMethod(clazz); + Map inputToField = getInputToField(clazz); + + Method finalResultMethod = findFinalResultMethod(clazz); + + return (T) executeWithDependencies(instance, finalResultMethod, operationToMethod, inputToField); + } + + private static Object executeWithDependencies(Object instance, + Method currentMethod, + Map operationToMethod, + Map inputToField) throws InvocationTargetException, IllegalAccessException { + List parameterValues = new ArrayList<>(currentMethod.getParameterCount()); + + for (Parameter parameter : currentMethod.getParameters()) { + Object value = null; + if (parameter.isAnnotationPresent(DependsOn.class)) { + String dependencyOperationName = parameter.getAnnotation(DependsOn.class).value(); + Method dependencyMethod = operationToMethod.get(dependencyOperationName); + + value = executeWithDependencies(instance, dependencyMethod, operationToMethod, inputToField); + } else if (parameter.isAnnotationPresent(Input.class)) { + String inputName = parameter.getAnnotation(Input.class).value(); + + Field field = inputToField.get(inputName); + field.setAccessible(true); + + value = field.get(instance); + } + + parameterValues.add(value); + } + + return currentMethod.invoke(instance, parameterValues.toArray()); + } + + private static Map getInputToField(Class clazz) { + Map inputNameToField = new HashMap<>(); + + for (Field field : clazz.getDeclaredFields()) { + if (!field.isAnnotationPresent(Input.class)) { + continue; + } + + Input input = field.getAnnotation(Input.class); + inputNameToField.put(input.value(), field); + } + + return inputNameToField; + } + + private static Map getOperationToMethod(Class clazz) { + Map operationNameToMethod = new HashMap<>(); + + for (Method method : clazz.getDeclaredMethods()) { + if (!method.isAnnotationPresent(Operation.class)) { + continue; + } + + Operation operation = method.getAnnotation(Operation.class); + + operationNameToMethod.put(operation.value(), method); + } + return operationNameToMethod; + } + + private static Method findFinalResultMethod(Class clazz) { + for (Method method : clazz.getDeclaredMethods()) { + if (method.isAnnotationPresent(FinalResult.class)) { + return method; + } + } + + throw new RuntimeException("No method found with FinalResult annotation"); + } +} diff --git a/src/main/java/reflections/topologicalsort/SqlQueryBuilder.java b/src/main/java/reflections/topologicalsort/SqlQueryBuilder.java new file mode 100644 index 0000000..c707c37 --- /dev/null +++ b/src/main/java/reflections/topologicalsort/SqlQueryBuilder.java @@ -0,0 +1,55 @@ +package reflections.topologicalsort; + +import reflections.topologicalsort.annotations.Annotations; + +import java.util.List; + +public class SqlQueryBuilder { + + @Annotations.Input("ids") + private List ids; + + @Annotations.Input("limit") + private Integer limit; + + @Annotations.Input("table") + private String tableName; + + @Annotations.Input("columns") + private List columnNames; + + public SqlQueryBuilder(List ids, Integer limit, String tableName, List columnNames) { + this.ids = ids; + this.limit = limit; + this.tableName = tableName; + this.columnNames = columnNames; + } + + @Annotations.Operation("SelectBuilder") + public String selectStatementBuilder(@Annotations.Input("table") String tableName, @Annotations.Input("columns") List columnNames) { + String columnsString = columnNames.isEmpty() ? "*" : String.join(",", columnNames); + + return String.format("SELECT %s FROM %s", columnsString, tableName); + } + + @Annotations.FinalResult + public String addWhereClause(@Annotations.DependsOn("SelectBuilder") String query, @Annotations.Input("ids") List ids) { + if (ids.isEmpty()) { + return query; + } + + return String.format("%s WHERE id IN (%s)", query, String.join(",", ids)); + } + + public String addLimit(@Annotations.DependsOn("SelectBuilder") String query, @Annotations.Input("limit") Integer limit) { + if (limit == null || limit == 0) { + return query; + } + + if (limit < 0) { + throw new RuntimeException("limit cannot be negative"); + } + + return String.format("%s LIMIT %d", query, limit.intValue()); + } +} diff --git a/src/main/java/reflections/topologicalsort/annotations/Annotations.java b/src/main/java/reflections/topologicalsort/annotations/Annotations.java new file mode 100644 index 0000000..808f76f --- /dev/null +++ b/src/main/java/reflections/topologicalsort/annotations/Annotations.java @@ -0,0 +1,34 @@ + + +package reflections.topologicalsort.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +public class Annotations { + + @Target({ElementType.FIELD, ElementType.PARAMETER}) + @Retention(RetentionPolicy.RUNTIME) + public @interface Input { + String value(); + } + + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + public @interface Operation { + String value(); + } + + @Target(ElementType.PARAMETER) + @Retention(RetentionPolicy.RUNTIME) + public @interface DependsOn { + String value(); + } + + @Target(ElementType.METHOD) + @Retention(RetentionPolicy.RUNTIME) + public @interface FinalResult { + } +} diff --git a/src/main/java/reflections/topologicalsort/databases/Database.java b/src/main/java/reflections/topologicalsort/databases/Database.java new file mode 100644 index 0000000..e49f04f --- /dev/null +++ b/src/main/java/reflections/topologicalsort/databases/Database.java @@ -0,0 +1,38 @@ + + +package reflections.topologicalsort.databases; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * Simulates database + */ +public class Database { + /// Game Name -> (Rating, Price) + private Map> GAME_TO_PRICE = Map.of("Fortnite", Arrays.asList(5f, 10f), + "Minecraft", Arrays.asList(4.3f, 100f), + "League Of Legends", Arrays.asList(4.9f, 89f), + "Ace Combat", Arrays.asList(4.8f, 50f), + "StarCraft", Arrays.asList(4.7f, 66f), + "Burnout", Arrays.asList(4.4f, 31f)); + + + public Set readAllGames() { + return Collections.unmodifiableSet(GAME_TO_PRICE.keySet()); + } + + public Map readGameToRatings(Set games) { + return GAME_TO_PRICE.entrySet() + .stream() + .filter(entry -> games.contains(entry.getKey())) + .collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, entry -> entry.getValue().get(0))); + } + + public Map readGameToPrice(Set games) { + return GAME_TO_PRICE.entrySet() + .stream() + .filter(entry -> games.contains(entry.getKey())) + .collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, entry -> entry.getValue().get(1))); + } +} diff --git a/src/main/java/segmentTree/SegmentTree.java b/src/main/java/segmentTree/SegmentTree.java new file mode 100644 index 0000000..176e9e8 --- /dev/null +++ b/src/main/java/segmentTree/SegmentTree.java @@ -0,0 +1,141 @@ +package segmentTree; + +// https://leetcode.com/problems/range-sum-query-mutable/ +public class SegmentTree { + private int[] tree; + private int n; + + public SegmentTree(int[] nums) { + n = nums.length; + tree = new int[n * 2]; + buildTree(nums); + } + + /** + * Suppose we have the array nums = [1, 3, 5, 7, 9, 11]. + * + * First, we determine n = 6 (the length of nums). + * We create tree with size 2 * n = 12. + * + * After the first loop: + * Copytree = [0, 0, 0, 0, 0, 0, 1, 3, 5, 7, 9, 11] + * 0 1 2 3 4 5 6 7 8 9 10 11 (indices) + * + * Now, the second loop builds the rest of the tree: + * + * tree[5] = tree[10] + tree[11] = 9 + 11 = 20 + * tree[4] = tree[8] + tree[9] = 5 + 7 = 12 + * tree[3] = tree[6] + tree[7] = 1 + 3 = 4 + * tree[2] = tree[4] + tree[5] = 12 + 20 = 32 + * tree[1] = tree[2] + tree[3] = 32 + 4 = 36 + * + * After the second loop: + * Copytree = [0, 36, 32, 4, 12, 20, 1, 3, 5, 7, 9, 11] + * 0 1 2 3 4 5 6 7 8 9 10 11 (indices) + * + * since we are leaving zeroth index empty, + * the left and right nodes are calculated as 2*i and 2*i+1 else it would be 2*i+1 and 2*i+2 + * @param nums + */ + private void buildTree(int[] nums) { + //This copies the elements from nums into the second half of tree. Here, n is the size of the original array. + for (int i = n, j = 0; i < 2 * n; i++, j++) { + tree[i] = nums[j]; + } + //This builds the rest of the tree from the bottom up. Each non-leaf node is the sum of its two children. + for (int i = n - 1; i > 0; --i) { + tree[i] = tree[i * 2] + tree[i * 2 + 1]; + } + } + + /** + * array nums = [1, 3, 5, 7, 9, 11]. + * Initial tree: + * [0, 36, 32, 4, 12, 20, 1, 3, 5, 7, 9, 11] + * 0 1 2 3 4 5 6 7 8 9 10 11 (indices) + * + * Suppose we want to update index 2 (third element in the original array) to value 8. + * + * treeIndex = 2 + 6 = 8 (as n = 6) + * Update leaf: tree[8] = 8 + * Move up: + * treeIndex = 8 / 2 = 4 + * tree[4] = tree[8] + tree[9] = 8 + 7 = 15 + * + * Move up again: + * treeIndex = 4 / 2 = 2 + * tree[2] = tree[4] + tree[5] = 15 + 20 = 35 + * + * Final move: + * treeIndex = 2 / 2 = 1 + * tree[1] = tree[2] + tree[3] = 35 + 4 = 39 + * + * @param index + * @param newValue + */ + public void update(int index, int newValue) { + // Convert to tree index + int treeIndex = index + n; //We add n to the input index to convert it to the corresponding index in the tree array. + + // Update the leaf node + tree[treeIndex] = newValue; + + // Update parent nodes + while (treeIndex > 1) { + treeIndex = treeIndex / 2; // Move to parent + int leftChild = 2 * treeIndex; + int rightChild = 2 * treeIndex + 1; + tree[treeIndex] = tree[leftChild] + tree[rightChild]; + } + } + + /** + * array nums = [1, 3, 5, 7, 9, 11]. + * Initial tree: + * [0, 36, 32, 4, 12, 20, 1, 3, 5, 7, 9, 11] + * 0 1 2 3 4 5 6 7 8 9 10 11 (indices) + * + * Suppose we want to query the sum of the range [1, 4] (2nd to 5th elements in the original array). + * Initialize: i = 1 + 6 = 7, j = 4 + 6 = 10, sum = 0 + * + * First iteration: + * i (7) is odd: sum += tree[7] = 3, i = 8 + * j (10) is even: sum += tree[10] = 9, j = 9 + * i = 8 / 2 = 4, j = 9 / 2 = 4 + * + * Second iteration: + * i == j == 4, so we add tree[4] = 15 to sum + * i = 4 / 2 = 2, j = 4 / 2 = 2 + * Loop ends as i == j == 2 + * The final sum is 3 + 9 + 15 = 27, which is indeed the sum of [3, 5, 8, 7] in the original array. + * + * @param i + * @param j + * @return + */ + public int sumRange(int i, int j) { + // We add n to both i and j to convert them to tree indices. + i += n; + j += n; + + int sum = 0; + + while (i <= j) { + if (i % 2 == 1) { //If i is odd, it's a right child. We include it in the sum and move to its right sibling. + sum += tree[i]; + i++; // Move to the j sibling + } + if (j % 2 == 0) { //If j is even, it's a left child. We include it in the sum and move to its left sibling. + sum += tree[j]; + j--; // Move to the i sibling + } + // Move up to the parent level + i /= 2; + j /= 2; + } + + return sum; + } + + +} diff --git a/src/main/java/sorting/InsertionSort.java b/src/main/java/sorting/InsertionSort.java new file mode 100644 index 0000000..fa4572a --- /dev/null +++ b/src/main/java/sorting/InsertionSort.java @@ -0,0 +1,23 @@ +package sorting; + +public class InsertionSort { + + public static void main(String[] args) { + int[] arr = {12, 11, 13, 5, 6}; + System.out.println(System.currentTimeMillis()); + + for (int i = 1; i < arr.length; i++) { + int index = i; + for (int j = i - 1; j >= 0; j--) { + if (arr[index] < arr[j]) { + int temp = arr[j]; + arr[j] = arr[index]; + arr[index] = temp; + index--; + } + } + } + System.out.println(System.currentTimeMillis()); + } + +} diff --git a/src/main/java/sorting/KthLargestElement.java b/src/main/java/sorting/KthLargestElement.java new file mode 100644 index 0000000..8d8434c --- /dev/null +++ b/src/main/java/sorting/KthLargestElement.java @@ -0,0 +1,62 @@ +package sorting; + +import java.util.*; + +public class KthLargestElement { + class Solution { + public int findKthLargest(int[] nums, int k) { + + return quickSelect(nums, 0, nums.length-1,nums.length-k); + } + + public void swap(int[] A, int i, int j) { + int temp = A[i]; + A[i] = A[j]; + A[j] = temp; + } + + public int partition(int[] nums, int start, int end) { + Random rand= new Random(); + //Get a random pivot between beg and end + int pivotIndex= start+rand.nextInt(Math.abs(end-start)); + + //New position of pivot element + int last=end; + + //Move the pivot element to right edge of the array + swap(nums,pivotIndex,end); + + end--; + + while(start<=end){ + if(nums[start]= 0; i--) + heapify(arr, i, n); + + for (int i = 0; i < n; i++) + System.out.print(arr[i] + " "); + + System.out.println(); + for (int i = n - 1; i >= 0; i--) { + int temp = arr[0]; + arr[0] = arr[i]; + arr[i] = temp; + + heapify(arr, 0, i); + } + + for (int i = 0; i < n; i++) + System.out.print(arr[i] + " "); + } + + private void heapify(int[] arr, int i, int n) { + int largest = i; + int left = 2 * i + 1; + int right = 2 * i + 2; + + if (left < n && arr[left] > arr[largest]) + largest = left; + if (right < n && arr[right] > arr[largest]) + largest = right; + if (largest != i) { + int temp = arr[largest]; + arr[largest] = arr[i]; + arr[i] = temp; + + heapify(arr, largest, n); + } + } +} \ No newline at end of file diff --git a/src/main/java/sorting/MinHeap.java b/src/main/java/sorting/MinHeap.java new file mode 100644 index 0000000..5abf4a3 --- /dev/null +++ b/src/main/java/sorting/MinHeap.java @@ -0,0 +1,225 @@ +package sorting; +/* + A min heap implementation + + Array Form: [ 5, 7, 6, 10, 15, 17, 12 ] + + Complete Binary Tree Form: + 5 + / \ + 7 6 + / \ / \ + 10 15 17 12 + + Mappings: + Parent -> (childIndex - 1) / 2 + Left Child -> 2 * parentIndex + 1 + Right Child -> 2 * parentIndex + 2 + + YouTube explanation: https://www.youtube.com/watch?v=g9YK6sftDi0 + Heap Sort explanation: https://www.youtube.com/watch?v=k72DtCnY4MU +*/ + +import java.util.*; + +public class MinHeap { + + public MinHeap() { + elements = new int[capacity]; + } + + private int capacity = 5; + private int[] elements; + private int size; + + public void add(int itemToAdd) { + ensureExtraCapacity(); + elements[size] = itemToAdd; + size++; + siftUp(); + } + + public boolean isEmpty() { + return size == 0; + } + + public int peek() { + if (isEmpty()) { + throw new NoSuchElementException("Heap is empty."); + } + + return elements[0]; + } + + public int remove() { + if (isEmpty()) { + throw new NoSuchElementException("Heap is empty."); + } + + int minItem = elements[0]; + elements[0] = elements[size - 1]; + size--; + + heapifyDown(); + + return minItem; + } + + + private void heapifyDown() { + + /* + We will bubble down the item just swapped to the "top" of the heap + after a removal operation to restore the heap + */ + int index = 0; + + /* + Since a binary heap is a complete binary tree, if we have no left child + then we have no right child. So we continue to bubble down as long as + there is a left child. + + A non-existent left child immediately tells us that a right child does + not exist. + */ + while (hasLeftChild(index)) { + /* + By default, assume that left child is smaller. If a right + child exists see if it can overtake the left child by + being smaller + */ + int smallerChildIndex = getLeftChildIndex(index); + + if (hasRightChild(index) && rightChild(index) < leftChild(index)) { + smallerChildIndex = getRightChildIndex(index); + } + + /* + If the item we are sitting on is < the smaller child then + nothing needs to happen & sifting down is finished. + + But if the smaller child is smaller than the node we are + holding, we should swap and continue sifting down. + */ + if (elements[index] < elements[smallerChildIndex]) { + break; + } else { + swap(index, smallerChildIndex); + } + + // Move to the node we just swapped down + index = smallerChildIndex; + } + } + + // Bubble up the item we inserted at the "end" of the heap + private void siftUp() { + /* + We will bubble up the item just inserted into to the "bottom" + of the heap after an insert operation. It will be at the last index + so index 'size' - 1 + */ + int index = size - 1; + + /* + While the item has a parent and the item beats its parent in + smallness, bubble this item up. + */ + while (hasParent(index) && elements[index] < parent(index)) { + swap(getParentIndex(index), index); + index = getParentIndex(index); + } + } + + /************************************************ + Helpers to access our array easily, perform + rudimentary operations, and manipulate capacity + ************************************************/ + + private void swap(int indexOne, int indexTwo) { + int temp = elements[indexOne]; + elements[indexOne] = elements[indexTwo]; + elements[indexTwo] = temp; + } + + // If heap is full then double capacity + private void ensureExtraCapacity() { + if (size == capacity) { + elements = Arrays.copyOf(elements, capacity * 2); + capacity *= 2; + } + } + + private int getLeftChildIndex(int parentIndex) { + return 2 * parentIndex + 1; + } + + private int getRightChildIndex(int parentIndex) { + return 2 * parentIndex + 2; + } + + private int getParentIndex(int childIndex) { + return (childIndex - 1) / 2; + } + + private boolean hasLeftChild(int index) { + return getLeftChildIndex(index) < size; + } + + private boolean hasRightChild(int index) { + return getRightChildIndex(index) < size; + } + + private boolean hasParent(int index) { + return index != 0 && getParentIndex(index) >= 0; + } + + private int leftChild(int index) { + return elements[getLeftChildIndex(index)]; + } + + private int rightChild(int index) { + return elements[getRightChildIndex(index)]; + } + + private int parent(int index) { + return elements[getParentIndex(index)]; + } + + /***********************************************/ + + private void printUnderlyingArray() { + System.out.print("[ "); + for (int item : elements) { + System.out.print(item + " "); + } + System.out.print("]"); + } + + public static void main(String args[]) { + MinHeap minHeap = new MinHeap(); + int[] insertItems = new int[]{0, 1, 3, 2, -4, 9, 1, 2}; + + for (int insertItem : insertItems) { + minHeap.add(insertItem); + System.out.println("Add " + insertItem); + System.out.println("Min is " + minHeap.peek()); + + minHeap.printUnderlyingArray(); + + System.out.println("\n"); + } + + System.out.println("\n\n"); + + for (int i = 0; i < insertItems.length; i++) { + System.out.println("Remove " + minHeap.remove()); + System.out.println("Min is " + minHeap.peek()); + + minHeap.printUnderlyingArray(); + + System.out.println("\n"); + } + } + +} \ No newline at end of file diff --git a/src/main/java/sorting/MinHeapTest.java b/src/main/java/sorting/MinHeapTest.java new file mode 100644 index 0000000..dffd2c4 --- /dev/null +++ b/src/main/java/sorting/MinHeapTest.java @@ -0,0 +1,107 @@ +package sorting; + +import java.util.NoSuchElementException; + +public class MinHeapTest { + + int[] elements; + int capacity; + int currentIndex; + + public MinHeapTest(int capacity) { + this.capacity = capacity; + this.elements = new int[capacity]; + } + + public void add(int value) { + currentIndex++; + if (currentIndex > capacity) { + currentIndex--; + return; + } + + elements[currentIndex] = value; + + verifyParent(currentIndex); + } + + public int peek() { + if (currentIndex < 0 || currentIndex > capacity) return Integer.MIN_VALUE; + return elements[0]; + } + + public int remove() { + if (isEmpty()) { + throw new NoSuchElementException("Heap is empty."); + } + int result = elements[0]; + currentIndex--; + elements[0] = elements[currentIndex]; + + verifyChild(); + return result; + } + + private boolean isEmpty() { + return currentIndex < 0; + } + + public void verifyParent(int currentIndex) { + int index = currentIndex; + + while (hasParent(index) && elements[index] < elements[getParentIndex(index)]) { + swap(index, getParentIndex(index)); + index = getParentIndex(index); + } + } + + private void swap(int index, int parentIndex) { + int temp = elements[index]; + elements[index] = elements[parentIndex]; + elements[parentIndex] = temp; + } + + private boolean hasParent(int index) { + return index != 0 || getParentIndex(index) >= 0; + } + + private int getParentIndex(int childIndex) { + return (childIndex - 1) / 2; + } + + public void verifyChild() { + int index = 0; + + while (hasLeftChildren(index)) { + + int minIndex = getLeftChildIndex(index); + if (hasRightChildren(index) && elements[getRightChildIndex(index)] < elements[minIndex]) { + minIndex = getRightChildIndex(index); + } + + if (elements[minIndex] < elements[index]) { + swap(index, minIndex); + } else { + break; + } + index = minIndex; + } + } + + private boolean hasRightChildren(int index) { + return getRightChildIndex(index) < capacity; + } + + private int getRightChildIndex(int parentIndex) { + return 2 * parentIndex + 2; + } + + private boolean hasLeftChildren(int index) { + return getLeftChildIndex(index) < capacity; + } + + private int getLeftChildIndex(int parentIndex) { + return 2 * parentIndex + 1; + } + +} diff --git a/src/main/java/sorting/PractiseMergeSort.java b/src/main/java/sorting/PractiseMergeSort.java new file mode 100644 index 0000000..07970f4 --- /dev/null +++ b/src/main/java/sorting/PractiseMergeSort.java @@ -0,0 +1,48 @@ +package sorting; + +import java.util.Arrays; + +public class PractiseMergeSort { + + public static void main(String[] args) { + int[] arr = { 9, 3, -1, 5, 2, 7 }; + int n = arr.length; + int[] temp = new int[n]; + mergeSort(arr, temp, 0, n - 1); + System.out.println(Arrays.toString(arr)); + } + + private static void mergeSort(int arr[], int temp[], int left, int right) { + while (left < right) { + int mid = ((right - left) / 2) + left; + mergeSort(arr, temp, left, mid); + mergeSort(arr, temp, mid + 1, right); + mergeUtil(arr, temp, left, mid, right); + } + } + + private static void mergeUtil(int[] arr, int[] temp, int left, int mid, int right) { + int i = left; + int j = mid + 1; + int k = left; + + while (i <= mid && j <= right) { + if (arr[i] < arr[j]) { + temp[k++] = arr[i++]; + } else { + temp[k++] = arr[j++]; + } + } + + while (i <= mid) { + temp[k++] = arr[i++]; + } + while (j <= right) { + temp[k++] = arr[j++]; + } + + for (int index = left; index <= right; index++) { + arr[index] = temp[index]; + } + } +} diff --git a/src/main/java/sorting/QuickSelect.java b/src/main/java/sorting/QuickSelect.java new file mode 100644 index 0000000..1866b44 --- /dev/null +++ b/src/main/java/sorting/QuickSelect.java @@ -0,0 +1,50 @@ +package sorting; + +import java.util.Random; + +class QuickSelect { + + public static void swap(int[] A, int i, int j) { + int temp = A[i]; + A[i] = A[j]; + A[j] = temp; + } + + public static int partition(int[] A, int low, int high, int pivotIndex) { + int pivot = A[pivotIndex]; + swap(A, pivotIndex, high); + int iLow = low; + for (int i = low; i < high; i++) { + if (A[i] <= pivot) { + swap(A, i, iLow); + iLow++; + } + } + swap(A, iLow, high); + return iLow; + } + + public static int quickSelect(int[] A, int left, int right, int k) { + if (left == right) { + return A[left]; + } + int pivotIndex = new Random().nextInt(right - left + 1) + left; + pivotIndex = partition(A, left, right, pivotIndex); + if (k == pivotIndex) { + return A[k]; + } else if (k < pivotIndex) { + return quickSelect(A, left, pivotIndex - 1, k); + } else { + return quickSelect(A, pivotIndex + 1, right, k); + } + } + + public static void main(String[] args) { + int[] A = { 7, 4, 6, 3, 9, 1 }; + int k = 2; + + System.out.print("K'th smallest element is " + quickSelect(A, 0, A.length - 1, k)); + } +} + + diff --git a/src/main/java/sorting/QuickSort.java b/src/main/java/sorting/QuickSort.java new file mode 100644 index 0000000..b439760 --- /dev/null +++ b/src/main/java/sorting/QuickSort.java @@ -0,0 +1,58 @@ +package sorting; + +import java.util.Random; + +public class QuickSort { + + public int[] sortArray(int[] nums) { + + quickSort(nums, 0, nums.length-1); + + return nums; +} + + public void quickSort(int[] nums, int start, int end){ + if(start + * Sometimes he puts emoticons within parentheses, + * and you find it hard to tell if a parenthesis really is a parenthesis or part of an emoticon. + + * A message has balanced parentheses if it consists of one of the following: + * - An empty string "" + * - One or more of the following characters: 'a' to 'z', ' ' (a space) or ':' (a colon) + * - An open parenthesis '(', followed by a message with balanced parentheses, followed by a close parenthesis ')'. + * - A message with balanced parentheses followed by another message with balanced parentheses. + * - A smiley face ":)" or a frowny face ":(" + * + * (:) : YES + * i am sick today (:() : YES + * )(: NO + * hacker cup: started :):): YES + * :((: NO + */ +public class BalancedSmiley { + + /** + * Idea : + * + * Idea is similar to other Balance Parentheses related problems. i.e we check the balance of ( and ) brackets. + * + * And whenever number of ) exceeds number of ( , we can say that it is unbalanced from start. + * + * And whenever number of ( exceeds number of ) , we can say that it is unbalanced from end ( this one is trivial). + * + * This can be done by incrementing when we see opening ( bracket and decrementing vice versa. + * + * + * if maxOpen is negative or minOpen isn't zero, it's not possible for the message to have balanced parentheses + * + * @param s + */ + public boolean isBalanced(String s) { + int minOpen = 0; + int maxOpen = 0; + + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) == '(') { + maxOpen++; + if (i == 0 || s.charAt(i - 1) == ':') { + minOpen++; + } + } else if (s.charAt(i) == ')') { + minOpen = Math.max(0, minOpen - 1); + if (i == 0 || s.charAt(i - 1) == ':') { + maxOpen--; + if (maxOpen < 0) break; + } + } + } + return maxOpen >= 0 && minOpen == 0; + } +} diff --git a/src/main/java/strings/parentheses/CanBeValid.java b/src/main/java/strings/parentheses/CanBeValid.java new file mode 100644 index 0000000..7817d33 --- /dev/null +++ b/src/main/java/strings/parentheses/CanBeValid.java @@ -0,0 +1,55 @@ +package strings.parentheses; + +/** + * https://leetcode.com/problems/check-if-a-parentheses-string-can-be-valid + * Idea : + * Idea is similar to other Balance Parentheses related problems. i.e we check the balance of ( and ) brackets. + * And whenever number of ) exceeds number of ( , we can say that it is unbalanced from start. + * And whenever number of ( exceeds number of ) , we can say that it is unbalanced from end ( this one is trivial). + * This can be done by incrementing when we see opening ( bracket and decrementing vice versa. + */ +public class CanBeValid { + + public boolean canBeValid(String s, String locked) { + int n = s.length(); + if (n % 2 != 0) return false; + + int open = 0; + int close = 0; + + // First check balance from left to right + // For opening '(' brackets + for (int i = 0; i < n; i++) { + + // If either char is ( or it is unlocked, + // We can increment open balance + if (locked.charAt(i) == '0' || s.charAt(i) == '(') open++; + else close++; // otherwise, decrement balance, since it is ) and also locked + + // Since balance is negative, we have more ')'. + // And there is no unlocked char available + // SO, it is invalid string for sure + if (close > open) return false; + } + + open = 0; + close = 0; + // Then also check balance from right to left + // For closing ')' brackets + for (int i = n - 1; i >= 0; i--) { + + // If either char is ) or it is unlocked, + // We can increment balance + if (locked.charAt(i) == '0' || s.charAt(i) == ')') close++; + else open++; + + // Since balance is negative, we have more '('. + // And there is no unlocked char available + // SO, it is invalid string for sure + + if (open > close) return false; + } + + return true; + } +} diff --git a/src/main/java/strings/parentheses/DifferentWaysToAddParenthesis.java b/src/main/java/strings/parentheses/DifferentWaysToAddParenthesis.java new file mode 100644 index 0000000..b78f4be --- /dev/null +++ b/src/main/java/strings/parentheses/DifferentWaysToAddParenthesis.java @@ -0,0 +1,61 @@ +package strings.parentheses; + +// Given a string of numbers and operators, +// return all possible results from computing all the different possible ways to group numbers +// and operators. The valid operators are +, - and *. + +import java.util.*; + +// Input: "2*3-4*5" +// Output: [-34, -14, -10, -10, 10] +// Explanation: +// (2*(3-(4*5))) = -34 +// ((2*3)-(4*5)) = -14 +// ((2*(3-4))*5) = -10 +// (2*((3-4)*5)) = -10 +// (((2*3)-4)*5) = 10 +public class DifferentWaysToAddParenthesis { + public List diffWaysToCompute(String input) { + if (input == null) return Collections.emptyList(); + + Map> map = new HashMap<>(); + return bfsHelper(input, map); + + } + + public List bfsHelper(String input, Map> map) { + + if (map.containsKey(input)) { + return map.get(input); + } + + List result = new ArrayList<>(); + if (!input.contains("+") && !input.contains("-") && !input.contains("*")) { + result.add(Integer.parseInt(input)); + } else { + // Split input string into two parts and solve them recursively + // for ex input = 2*3-4 output=2,-2 + // 2* | 3-4 => this right and left expression returns a list + // 2*3| -4 => this right and left expression returns another list + for (int i = 0; i < input.length(); i++) { + if (!Character.isDigit(input.charAt(i))) { + List firstList = bfsHelper(input.substring(0, i), map); + List secondList = bfsHelper(input.substring(i + 1), map); + for (int first : firstList) { + for (int second : secondList) { + if (input.charAt(i) == '+') { + result.add(first + second); + } else if (input.charAt(i) == '-') { + result.add(first - second); + } else { + result.add(first * second); + } + } + } + } + } + } + map.put(input, result); + return result; + } +} \ No newline at end of file diff --git a/src/main/java/strings/parentheses/GenerateParenthesis.java b/src/main/java/strings/parentheses/GenerateParenthesis.java new file mode 100644 index 0000000..5f4cf1c --- /dev/null +++ b/src/main/java/strings/parentheses/GenerateParenthesis.java @@ -0,0 +1,44 @@ +package strings.parentheses; + +import java.util.ArrayList; +import java.util.List; + +/** + * https://leetcode.com/problems/generate-parentheses/ + * Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses. + * Input: n = 3 + * Output: ["((()))","(()())","(())()","()(())","()()()"] + */ +class GenerateParenthesis { + + public List generateParenthesis(int n) { + List result = new ArrayList<>(); + if (n == 0) + return result; + // initially we send empty string and target number of pairs needed + generateUtil(n, new StringBuilder(), 0, 0, result); + + return result; + } + + public void generateUtil(int n, StringBuilder paran, int open, int close, List result) { + if (close == n) { // when close reaches n, we know n pairs have been created + result.add(paran.toString()); + return; + } + + if (close < open) { // when close is less than open we add a close and proceed + paran.append(")"); + generateUtil(n, paran, open, close + 1, result); + paran.deleteCharAt(paran.length() - 1); // backtracking to remove the last seen + } + if (open < n) { + paran.append("("); + generateUtil(n, paran, open + 1, close, result); + paran.deleteCharAt(paran.length() - 1); + + } + + } + +} \ No newline at end of file diff --git a/src/main/java/strings/parentheses/LongestValidParentheses.java b/src/main/java/strings/parentheses/LongestValidParentheses.java new file mode 100644 index 0000000..2ffba04 --- /dev/null +++ b/src/main/java/strings/parentheses/LongestValidParentheses.java @@ -0,0 +1,51 @@ +package strings.parentheses; + +/** + * https://leetcode.com/problems/longest-valid-parentheses/submissions/ + */ +public class LongestValidParentheses { + + public int longestValidParentheses(String s) { + + int open = 0; + int close = 0; + int maxLen = 0; + + for (int i = 0; i < s.length(); i++) { + + if (s.charAt(i) == '(') { + open++; + } else { + close++; + } + + if (open == close) { + maxLen = Math.max(open + close, maxLen); + } + if (close > open) { + open = 0; + close = 0; + } + } + open = 0; + close = 0; + for (int i = s.length() - 1; i >= 0; i--) { + + if (s.charAt(i) == '(') { + open++; + } else { + close++; + } + + if (open == close) { + maxLen = Math.max(open + close, maxLen); + } + if (open > close) { + open = 0; + close = 0; + } + } + + return maxLen; + } +} diff --git a/src/main/java/strings/parentheses/MinSwapsToBalance.java b/src/main/java/strings/parentheses/MinSwapsToBalance.java new file mode 100644 index 0000000..eb6d936 --- /dev/null +++ b/src/main/java/strings/parentheses/MinSwapsToBalance.java @@ -0,0 +1,75 @@ +package strings.parentheses; + +import java.util.ArrayList; +import java.util.List; + +/** + * Idea : + * + * Idea is similar to other Balance Parentheses related problems. i.e we check the balance of ( and ) brackets. + * + * And whenever number of ) exceeds number of ( , we can say that it is unbalanced from start. + * + * And whenever number of ( exceeds number of ) , we can say that it is unbalanced from end ( this one is trivial). + * + * This can be done by incrementing when we see opening ( bracket and decrementing vice versa. + */ +public class MinSwapsToBalance { + public static long swapCount(String s) { + + // Keep track of '[' + List pos = new ArrayList<>(); + for (int i = 0; i < s.length(); ++i) + if (s.charAt(i) == '[') + pos.add(i); + + // To OpenBraceCount number of encountered '[' + int OpenBraceCount = 0; + + // To track position of next '[' in pos + int nextPosOfOpen = 0; + + // To store result + long sum = 0; + + char[] S = s.toCharArray(); + + for (int i = 0; i < s.length(); ++i) { + + // Increment OpenBraceCount and move nextPosOfOpen + // to next position + if (S[i] == '[') { + ++OpenBraceCount; + ++nextPosOfOpen; + } else if (S[i] == ']') + --OpenBraceCount; + + // We have encountered an + // unbalanced part of string + if (OpenBraceCount < 0) { + + // Increment sum by number of + // swaps required i.e. position + // of next '[' - current position + sum += pos.get(nextPosOfOpen) - i; + char temp = S[i]; + S[i] = S[pos.get(nextPosOfOpen)]; + S[pos.get(nextPosOfOpen)] = temp; + ++nextPosOfOpen; + + // Reset OpenBraceCount to 1 + OpenBraceCount = 1; + } + } + return sum; + } + + // Driver code + public static void main(String[] args) { + String s = "[]][]["; + System.out.println(swapCount(s)); + + s = "[[][]]"; + System.out.println(swapCount(s)); + } +} diff --git a/src/main/java/strings/parentheses/RemoveInvalidParentheses.java b/src/main/java/strings/parentheses/RemoveInvalidParentheses.java new file mode 100644 index 0000000..b472bcc --- /dev/null +++ b/src/main/java/strings/parentheses/RemoveInvalidParentheses.java @@ -0,0 +1,100 @@ +package strings.parentheses; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class RemoveInvalidParentheses { + public List removeInvalidParentheses(String s) { + List res = new ArrayList<>(); + + // sanity check + if (s == null) return res; + + Set visited = new HashSet<>(); + Deque queue = new ArrayDeque<>(); + + // initialize + queue.add(s); + visited.add(s); + + boolean found = false; + + while (!queue.isEmpty()) { + s = queue.poll(); + + if (isValid(s)) { + // found an answer, add to the result + res.add(s); + found = true; + } + /** + * this ensures once we've found a valid parentheses pattern, + * we don't do any further bfs using items pending in the queue since any further bfs would only yield strings of smaller length. + * However the items already in queue need to be processed since there could be other solutions of the same length. + * great solution. + */ + if (found) continue; + + // generate all possible states + for (int i = 0; i < s.length(); i++) { + // we only try to remove left or right paren + if (s.charAt(i) != '(' && s.charAt(i) != ')') continue; + + String t = s.substring(0, i) + s.substring(i + 1); + + if (!visited.contains(t)) { + // for each state, if it's not visited, add it to the queue + queue.add(t); + visited.add(t); + } + } + } + + return res; + } + + // helper function checks if string s contains valid parentheses + boolean isValid(String s) { + int count = 0; + + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == '(') count++; + if (c == ')' && count-- == 0) return false; + } + + return count == 0; + } + + + public List removeInvalidParenthesesDFS(String s) { + List output = new ArrayList<>(); + removeHelper(s, output, 0, 0, '(', ')'); + return output; + } + + public void removeHelper(String s, List output, int iStart, int jStart, char openParen, char closedParen) { + int numOpenParen = 0, numClosedParen = 0; + for (int i = iStart; i < s.length(); i++) { + if (s.charAt(i) == openParen) numOpenParen++; + if (s.charAt(i) == closedParen) numClosedParen++; + if (numClosedParen > numOpenParen) { // We have an extra closed paren we need to remove + for (int j = jStart; j <= i; j++) // Try removing one at each position, skipping duplicates + if (s.charAt(j) == closedParen && (j == jStart || s.charAt(j - 1) != closedParen)) + // Recursion: iStart = i since we now have valid # closed parenthesis thru i. jStart = j prevents duplicates + removeHelper(s.substring(0, j) + s.substring(j + 1, s.length()), output, i, j, openParen, closedParen); + return; // Stop here. The recursive calls handle the rest of the string. + } + } + // No invalid closed parenthesis detected. Now check opposite direction, or reverse back to original direction. + String reversed = new StringBuilder(s).reverse().toString(); + if (openParen == '(') + removeHelper(reversed, output, 0, 0, ')', '('); + else + output.add(reversed); + } +} diff --git a/src/main/java/strings/parentheses/RemoveOuterParentheses.java b/src/main/java/strings/parentheses/RemoveOuterParentheses.java new file mode 100644 index 0000000..08c4d18 --- /dev/null +++ b/src/main/java/strings/parentheses/RemoveOuterParentheses.java @@ -0,0 +1,31 @@ +package strings.parentheses; + +// https://leetcode.com/problems/remove-outermost-parentheses/ +public class RemoveOuterParentheses { + + /** + * opened count the number of opened parenthesis. + * Add every char to the result, + * unless the first left parenthesis, + * and the last right parenthesis. + * @param s + * @return + */ + public String removeOuterParentheses(String s) { + int opened = 0; + StringBuilder sb = new StringBuilder(); + + for (int i = 0; i < s.length(); i++) { + char ch = s.charAt(i); + if (ch == '(') { + if (opened > 0) sb.append(ch); + opened++; + + } else { + if (opened > 1) sb.append(ch); + opened--; + } + } + return sb.toString(); + } +} diff --git a/src/main/java/strings/parentheses/ScoreOfParentheses.java b/src/main/java/strings/parentheses/ScoreOfParentheses.java new file mode 100644 index 0000000..f514af4 --- /dev/null +++ b/src/main/java/strings/parentheses/ScoreOfParentheses.java @@ -0,0 +1,43 @@ +package strings.parentheses; + +import java.util.ArrayDeque; +import java.util.Deque; + +/** + * Idea : + *

+ * Idea is similar to other Balance Parentheses related problems. i.e we check the balance of ( and ) brackets. + *

+ * And whenever number of ) exceeds number of ( , we can say that it is unbalanced from start. + *

+ * And whenever number of ( exceeds number of ) , we can say that it is unbalanced from end ( this one is trivial). + *

+ * This can be done by incrementing when we see opening ( bracket and decrementing vice versa. + */ +public class ScoreOfParentheses { + public int scoreOfParentheses(String s) { + int cur = 0, result = 0; + Deque stack = new ArrayDeque<>(); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == '(') { + //cur is local variable. This line stores cur so that it could be retrieved later + stack.push(cur); + //From here a new function call starts. So reset cur + cur = 0; + } else if (c == ')') { + //Retrieve cur + cur = stack.pop(); + //calculate result based on result + if (s.charAt(i - 1) == '(') { + cur += 1; + } else { + cur += result * 2; + } + //store intermediate result + result = cur; + } + } + return result; + } +} diff --git a/src/geeksforgeeks/ValidParentheses.java b/src/main/java/strings/parentheses/ValidParentheses.java similarity index 95% rename from src/geeksforgeeks/ValidParentheses.java rename to src/main/java/strings/parentheses/ValidParentheses.java index 9ba20a0..4067a1b 100644 --- a/src/geeksforgeeks/ValidParentheses.java +++ b/src/main/java/strings/parentheses/ValidParentheses.java @@ -1,4 +1,4 @@ -package geeksforgeeks; +package strings.parentheses; import java.util.Stack; diff --git a/src/main/java/strings/parentheses/ValidParenthesesString.java b/src/main/java/strings/parentheses/ValidParenthesesString.java new file mode 100644 index 0000000..38a9f27 --- /dev/null +++ b/src/main/java/strings/parentheses/ValidParenthesesString.java @@ -0,0 +1,82 @@ +package strings.parentheses; + +/** + * tricky braces + * + * https://leetcode.com/problems/valid-parenthesis-string/discuss/543521/Java-Count-Open-Parenthesis-O(n)-time-O(1)-space-Picture-Explain + */ +public class ValidParenthesesString { + public boolean checkValidString(String s) { + int cmin = 0; + int cmax = 0; // open parentheses count in range [cmin, cmax] + for (char c : s.toCharArray()) { + if (c == '(') { + cmax++; + cmin++; + } else if (c == ')') { + cmax--; + cmin--; + } else if (c == '*') { + cmax++; // if `*` become `(` then openCount++ + cmin--; // if `*` become `)` then openCount-- + // if `*` become `` then nothing happens + // So openCount will be in new range [cmin-1, cmax+1] + } + + /** + * Case - 1: + * If cmax < 0, the number of ')' is lesser than 0. We immediately return false. + * Why : Let's take an example "())", in this case, cmax would be less than 0 because we have two ' )' and only one '('. + * Now irrespective of how many '*' we have, this sequence is already invalid, hence we return false. + */ + if (cmax < 0) { + return false; // Currently, don't have enough open parentheses to match close parentheses-> Invalid + } + + /** + * Case - 2: + * cmin = Math.max(cmin, 0) + * + * The way I got to wrap my head around this was: + * Cmin and Cmax are both subtracted by 1, whenever we encounter a ")". + * Therefore, Case -1 covers the case in which we have more ")" than "(". + * Now the additional case we have to look at is, when we have extra ")", which we can account to the "*" [Since we do --cmin here]. + * + * However, we can just ignore the "*" as empty strings in this case. + * Example: "( ) * * " + * cmax = 1 0 1 2 + * cmin = 1 0 0 0 -> We don't want the last two to become 1 0 -1 -2 + * + * We can see that the cmin values would become -1 and -2 for the last two "*". + * However this would mean we would be adding additional ")", which makes the sequence "()))". + * This is not a right sequence. Therefore, we must keep them as empty strings. + * Hence we do a max with 0, which implies that if we have additional "*", we don't take them as ")", instead we treat them as empty strings. + */ + // For example: ())( + cmin = Math.max(cmin, 0); // It's invalid if open parentheses count < 0 that's why cmin can't be negative + } + return cmin == 0; // Return true if can found `openCount == 0` in range [cmin, cmax] + } + + public boolean checkValidStringAnother(String s) { + if (s.length() < 1) return true; + + int balance = 0; + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) == ')') balance--; + else balance++; + + if (balance < 0) return false; + } + + if (balance == 0) return true; + balance = 0; + for (int i = s.length() - 1; i >= 0; i--) { + if (s.charAt(i) == '(') balance--; + else balance++; + if (balance < 0) return false; + + } + return true; + } +} diff --git a/src/main/java/strings/stringProblems/Combination.java b/src/main/java/strings/stringProblems/Combination.java new file mode 100644 index 0000000..ed32cf6 --- /dev/null +++ b/src/main/java/strings/stringProblems/Combination.java @@ -0,0 +1,54 @@ +package strings.stringProblems; + +import java.util.*; + +public class Combination { + + public void combination(char input[]) { + Map countMap = new TreeMap<>(); + for (char ch : input) { + countMap.compute(ch, (key, val) -> { + if (val == null) { + return 1; + } else { + return val + 1; + } + }); + } + char str[] = new char[countMap.size()]; + int count[] = new int[countMap.size()]; + int index = 0; + for (Map.Entry entry : countMap.entrySet()) { + str[index] = entry.getKey(); + count[index] = entry.getValue(); + index++; + } + char[] output = new char[input.length]; + combination(str, count, 0, output, 0); + } + + private void combination(char[] str, int[] count, int level, char[] result, int pos) { + print(result, level); + for (int i = pos; i < str.length; i++) { + if (count[i] != 0) { + result[level] = str[i]; + count[i]--; + combination(str, count, level + 1, result, pos); + count[i]++; + } + } + } + + private void print(char result[], int pos) { + for (int i = 0; i < pos; i++) { + System.out.print(result[i] + " "); + } + System.out.println(); + } + + public static void main(String args[]) { + Combination c = new Combination(); + c.combination("abc".toCharArray()); + } + +} \ No newline at end of file diff --git a/src/main/java/strings/stringProblems/CustomSortString.java b/src/main/java/strings/stringProblems/CustomSortString.java new file mode 100644 index 0000000..1af83a2 --- /dev/null +++ b/src/main/java/strings/stringProblems/CustomSortString.java @@ -0,0 +1,54 @@ +package strings.stringProblems; + +import java.util.Arrays; + +public class CustomSortString { + + public String customSortStringApproach1(String order, String str) { + int[] cache = new int[26]; + //Arrays.fill(cache,Integer.MAX_VALUE-1); + for (int i = 0; i < order.length(); i++) { + cache[order.charAt(i) - 'a'] = i; + } + Character[] tmpArr = new Character[str.length()]; + + for (int i = 0; i < str.length(); i++) { + tmpArr[i] = str.charAt(i); + } + Arrays.sort(tmpArr, (a, b) -> + Integer.compare(cache[a - 'a'], cache[b - 'a']) + ); + StringBuilder sb = new StringBuilder(); + for (Character i : tmpArr) { + sb.append(i); + } + return sb.toString(); + } + + public String customSortString(String order, String str) { + int[] cache = new int[26]; + // take target's char counts + for (char c : str.toCharArray()) { + cache[c - 'a']++; + } + StringBuilder sb = new StringBuilder(); + // greedily populate the order chars as first + for (char c : order.toCharArray()) { + if (cache[c - 'a'] == 0) continue; + int occurence = cache[c - 'a']; + while (occurence-- > 0) { + sb.append(c); + } + cache[c - 'a'] = 0; + } + // then populate the non order chars + for (int i = 0; i < 26; i++) { + int occurrence = cache[i]; + while (occurrence-- > 0) { + sb.append((char) (i + 'a')); + } + cache[i] = 0; + } + return sb.toString(); + } +} diff --git a/src/main/java/strings/stringProblems/DistinctSubstring.java b/src/main/java/strings/stringProblems/DistinctSubstring.java new file mode 100644 index 0000000..5e5290a --- /dev/null +++ b/src/main/java/strings/stringProblems/DistinctSubstring.java @@ -0,0 +1,33 @@ +package strings.stringProblems; + +import java.util.ArrayList; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; + +public class DistinctSubstring { + + public static List kSubstring(String s, int k) { + Set window = new LinkedHashSet<>(); + Set result = new LinkedHashSet<>(); + for (int start = 0, end = 0; end < s.length(); end++) { + while (window.contains(s.charAt(end))) { + window.remove(s.charAt(start)); + start++; + } + + window.add(s.charAt(end)); + + if (window.size() == k) { + result.add(s.substring(start, end + 1)); + window.remove(s.charAt(start)); + start++; + } + } + return new ArrayList<>(result); + } + + public static void main(String[] args) { + System.out.println(kSubstring("awaglknagawunagwkwagl", 4)); + } +} \ No newline at end of file diff --git a/src/main/java/strings/stringProblems/FindLongestStringInArray.java b/src/main/java/strings/stringProblems/FindLongestStringInArray.java new file mode 100644 index 0000000..357b31b --- /dev/null +++ b/src/main/java/strings/stringProblems/FindLongestStringInArray.java @@ -0,0 +1,26 @@ +package strings.stringProblems; + +import java.util.Arrays; +import java.util.Comparator; + +public class FindLongestStringInArray { + + public static void main(String[] args) { + String arr[] = { "geeks", "for", "geeksfor", "practiceproblems"}; + StringComparator str = new StringComparator(); + Arrays.sort(arr, str); + System.out.println(Arrays.toString(arr)); + } +} + +class StringComparator implements Comparator { + + @Override + public int compare(String o1, String o2) { + if (o1.length() > o2.length()) { + return -1; + } + return 0; + } + +} \ No newline at end of file diff --git a/src/main/java/strings/stringProblems/LongestCommonPrefix.java b/src/main/java/strings/stringProblems/LongestCommonPrefix.java new file mode 100644 index 0000000..3683619 --- /dev/null +++ b/src/main/java/strings/stringProblems/LongestCommonPrefix.java @@ -0,0 +1,39 @@ +package strings.stringProblems; + +import java.util.Arrays; + +public class LongestCommonPrefix { + + /** + * The first thing to understand is that the longest common prefix can only be as long as the shortest string with a commo prefix in the array. + * So, when we sort, the shortest string with a common prefix will be the first string (assuming ascending order). + * + * Then, we have to understand that the longest common prefix must apply for ALL array elements, + * if there's an array element that does not have the longest common prefix you've found so far, then there is no prefix, it's empty string. + * So, for example, if the first string is "aaa" and last string comes out to be "baa", then there is no common prefix. + * + * ["HAM",.........."HAMPER"] + * So, here the first and only the last is compared because if all the middle starts with "HAM" and even the last one is "HAM ", + * that means all the middle one would have had "HAM" in them. + * + * Arrays.Sort sorts the string in a lexicographical order. Which means that the order would be following: + * AAA + * AAAA + * AAB + * BAA + */ + public String longestCommonPrefix(String[] strs) { + if (strs == null || strs.length == 0) return ""; + if (strs.length == 1) return strs[0]; + + StringBuilder sb = new StringBuilder(); + Arrays.sort(strs); + char[] first = strs[0].toCharArray(); + char[] last = strs[strs.length - 1].toCharArray(); + for (int i = 0, j = 0; i < first.length && j < last.length; i++, j++) { + if (first[i] != last[j]) break; + sb.append(first[i]); + } + return sb.toString(); + } +} diff --git a/src/main/java/strings/stringProblems/MostCommonWord.java b/src/main/java/strings/stringProblems/MostCommonWord.java new file mode 100644 index 0000000..d0bee28 --- /dev/null +++ b/src/main/java/strings/stringProblems/MostCommonWord.java @@ -0,0 +1,77 @@ +package strings.stringProblems; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +class MostCommonWord { + public String mostCommonWord(String paragraph, String[] banned) { + + String[] arr = paragraph.split("[\\s,]+"); + boolean isBanned = banned == null || banned.length == 0 ? true : false; + Map map = new HashMap(); + for (int i = 0; i < arr.length; i++) { + String val = arr[i].toLowerCase().trim().replaceAll("[^a-z]", ""); + map.put(val, map.getOrDefault(val, 0) + 1); + } + int max = 0; + String result = ""; + for (Map.Entry entry : map.entrySet()) { + boolean isBannedWord = false; + if (entry.getValue() > max) { + if (!isBanned) { + for (int i = 0; i < banned.length; i++) { + if (banned[i].equals(entry.getKey())) { + isBannedWord = true; + break; + } + } + } + if (!isBannedWord) { + max = entry.getValue(); + result = entry.getKey(); + } + } + } + return result; + } + + public String mostCommonWordLeetCode(String paragraph, String[] banned) { + paragraph += "."; + + Set banset = new HashSet(); + for (String word : banned) + banset.add(word); + Map count = new HashMap(); + + String ans = ""; + int ansfreq = 0; + + StringBuilder word = new StringBuilder(); + for (char c : paragraph.toCharArray()) { + if (Character.isLetter(c)) { + word.append(Character.toLowerCase(c)); + } else if (word.length() > 0) { + String finalword = word.toString(); + if (!banset.contains(finalword)) { + count.put(finalword, count.getOrDefault(finalword, 0) + 1); + if (count.get(finalword) > ansfreq) { + ans = finalword; + ansfreq = count.get(finalword); + } + } + word = new StringBuilder(); + } + } + + return ans; + } + + public static void main(String[] args) { + String paragraph = "Bob hit a ball, the hit BALL flew far after it was hit."; + String[] banned = { "hit" }; + MostCommonWord mcw = new MostCommonWord(); + System.out.println(mcw.mostCommonWord(paragraph, banned)); + } +} \ No newline at end of file diff --git a/src/main/java/strings/stringProblems/PermutationAndCombination.java b/src/main/java/strings/stringProblems/PermutationAndCombination.java new file mode 100644 index 0000000..0f1ccec --- /dev/null +++ b/src/main/java/strings/stringProblems/PermutationAndCombination.java @@ -0,0 +1,91 @@ +package strings.stringProblems; + +import java.util.Map; +import java.util.TreeMap; + +public class PermutationAndCombination { + + public static void main(String args[]) { + PermutationAndCombination sp = new PermutationAndCombination(); + sp.permute("ABC".toCharArray()); + + } + + private void permute(char[] charArray) { + Map map = new TreeMap<>(); + for (char ch : charArray) { + map.compute(ch, (key, val) -> { + if (val == null) { + return 1; + } else { + return val + 1; + } + }); + } + + char[] str = new char[charArray.length]; + int[] count = new int[charArray.length]; + int index = 0; + for (Map.Entry ch : map.entrySet()) { + str[index] = ch.getKey(); + count[index] = ch.getValue(); + index++; + } + + char[] result = new char[charArray.length]; + + permutate(str, count, 0, result); + System.out.println("++++++++++++++++"); + combination(str, count, 0, new char[charArray.length], 0); + System.out.println("++++++++++++++++"); + permutationAndCombination(str, count, 0, result); + + } + + private void permutate(char[] str, int[] count, int level, char[] result) { + if (level == result.length) { + System.out.println(result); + return; + } + + for (int i = 0; i < str.length; i++) { + if (count[i] != 0) { + result[level] = str[i]; + count[i]--; + permutate(str, count, level + 1, result); + count[i]++; + } + } + } + + private void print(char[] result, int level) { + for (int i = 0; i < level; i++) { + System.out.print(result[i] + " "); + } + System.out.println(); + } + + private void permutationAndCombination(char[] str, int[] count, int level, char[] result) { + print(result, level); + for (int i = 0; i < str.length; i++) { + if (count[i] != 0) { + result[level] = str[i]; + count[i]--; + permutationAndCombination(str, count, level + 1, result); + count[i]++; + } + } + } + + private void combination(char[] str, int[] count, int level, char[] result, int pos) { + print(result, level); + for (int i = pos; i < str.length; i++) { + if (count[i] != 0) { + result[level] = str[i]; + count[i]--; + combination(str, count, level + 1, result, i); + count[i]++; + } + } + } +} diff --git a/src/main/java/strings/stringProblems/PushDominoes.java b/src/main/java/strings/stringProblems/PushDominoes.java new file mode 100644 index 0000000..e7582b5 --- /dev/null +++ b/src/main/java/strings/stringProblems/PushDominoes.java @@ -0,0 +1,112 @@ +package strings.stringProblems; + +import java.util.Arrays; + +/** + * https://leetcode.com/problems/push-dominoes + */ +public class PushDominoes { + + /** + * // if there is NO dot (meaning every domino is pushed), then the final state is the initial state + * // for example, all "LLL" => "LLL", all "RRR" => "RRR", + * // mixed "LLLRR" => "LLLRR" (falling domino having no effect on already-fallen domino) + * // if there is only ONE dot, we have a few possibilities: + * // "L.R" => "L.R", "R.L" => "R.L" (center-one standing), "L.L" => "LLL", "R.R" => "RRR" + * // if there are Three dots, we have the following possibilities: + * // "L..R" => "L..R", "R..L" => "RRLL" (center-one standing), "L..L" => "LLLL", "R..R" => "RRRR" + * // if there are TWO dots, we have the following possibilities: + * // "L...R" => "L...R", "R...L" => "RR.LL" (center-one standing), "L...L" => "LLLLL", "R...R" => "RRRRR" + * // Therefore the rule is: + * // for each region of dots (a substring "P....Q"), check its left and right + * // if left is "L" and right is "R", the substring remains as is; + * // if left is "R" and right is "L", the substring is converted into either "RRRLLL" or "RR.LL" + * // if left and right are the same, the substring is converted into either "RRRRR" or "LLLLL" + * + * @param dominoes + * @return + */ + public String pushDominoes(String dominoes) { + // place a sentinel "L" + dominoes + "R", as this does not impact the final outcome + dominoes = "L" + dominoes + "R"; + char[] dom = dominoes.toCharArray(); + + int i = 0; // slow pointer + int j = 0; + while (i < dominoes.length() - 1) { + j = i + 1; // fast pointer + while (j < dominoes.length() && dominoes.charAt(j) == '.') j++; + + if (dom[i] == dom[j]) Arrays.fill(dom, i, j, dom[i]); + + else if (dom[i] == 'R' && dom[j] == 'L') { + int effect = (j - i - 1) / 2; + /** + * // half R + * for (int count = 0; count < effect / 2; count++) { + * builder.append('R'); + * } + * if (1 == countDots % 2) { + * // center dot + * builder.append('.'); + * } + * // half L + * for (int count = 0; count < effect / 2; count++) { + * builder.append('L'); + * } + */ + Arrays.fill(dom, i, i + effect + 1, 'R'); + Arrays.fill(dom, j - effect, j + 1, 'L'); + } + + i = j; + } + + return new String(Arrays.copyOfRange(dom, 1, dom.length - 1)); + } + + public String pushDominoesAnother(String dominoes) { + int[] leftForce = new int[dominoes.length()]; + int[] rightForce = new int[dominoes.length()]; + int lastSeen = -1; + for (int i = dominoes.length() - 1; i >= 0; i--) { + if (dominoes.charAt(i) == 'L') lastSeen = i; + else if (dominoes.charAt(i) == 'R') lastSeen = -1; + + leftForce[i] = lastSeen; + } + lastSeen = -1; + for (int i = 0; i < dominoes.length(); i++) { + if (dominoes.charAt(i) == 'R') lastSeen = i; + else if (dominoes.charAt(i) == 'L') lastSeen = -1; + + rightForce[i] = lastSeen; + } + char[] result = new char[dominoes.length()]; + + for (int i = 0; i < dominoes.length(); i++) { + if (dominoes.charAt(i) == '.') { + + int nearestLeft = leftForce[i]; + int nearestRight = rightForce[i]; + + int leftDiff = nearestLeft == -1 ? Integer.MAX_VALUE : Math.abs(nearestLeft - i); + int rightDiff = nearestRight == -1 ? Integer.MAX_VALUE : Math.abs(nearestRight - i); + + if (leftDiff == rightDiff) { + result[i] = '.'; + } else if (leftDiff < rightDiff) { + result[i] = 'L'; + } else if (leftDiff > rightDiff) { + result[i] = 'R'; + } + + } else { + result[i] = dominoes.charAt(i); + } + + } + + return new String(result); + } +} diff --git a/src/main/java/strings/stringProblems/RearrangeCharactersInString.java b/src/main/java/strings/stringProblems/RearrangeCharactersInString.java new file mode 100644 index 0000000..8e88e0c --- /dev/null +++ b/src/main/java/strings/stringProblems/RearrangeCharactersInString.java @@ -0,0 +1,61 @@ +package strings.stringProblems; + +import java.util.HashMap; +import java.util.Map; +import java.util.PriorityQueue; + +/** + * + */ +public class RearrangeCharactersInString { + + public static String rearrangeString(String S) { + if (S == null || S.length() == 0) { + return ""; + } + Map map = new HashMap<>(); + for (char c : S.toCharArray()) { + map.put(c, map.getOrDefault(c, 0) + 1); + } + + PriorityQueue> pq = new PriorityQueue<>((a, b) -> (b.getValue() - a.getValue())); + for (Map.Entry entry : map.entrySet()) { + pq.offer(entry); + } + + StringBuilder sb = new StringBuilder(); + while (!pq.isEmpty()) { + Map.Entry first = pq.poll(); + + if (sb.length() == 0 || first.getKey() != sb.charAt(sb.length() - 1)) { + sb.append(first.getKey()); + first.setValue(first.getValue() - 1); + + if (first.getValue() != 0) { + pq.offer(first); + } + } else { + Map.Entry second = pq.poll(); + // we do not have any other characters that different with current + // string tail + if (second == null) { + return ""; + } + sb.append(second.getKey()); + second.setValue(second.getValue() - 1); + + if (second.getValue() != 0) { + pq.offer(second); + } + // DO NOT FORGET to push top frequency entry into queue as well + pq.offer(first); + } + } + return sb.toString(); + } + + public static void main(String args[]) { + String str = "bbbaa"; + System.out.println(rearrangeString(str)); + } +} diff --git a/src/main/java/strings/stringProblems/ShiftingLetters.java b/src/main/java/strings/stringProblems/ShiftingLetters.java new file mode 100644 index 0000000..1e7db0a --- /dev/null +++ b/src/main/java/strings/stringProblems/ShiftingLetters.java @@ -0,0 +1,36 @@ +package strings.stringProblems; + +public class ShiftingLetters { + public String shiftingLetters(String s, int[] shifts) { + int total = 0; + shifts[shifts.length - 1] %= 26; + for (int i = shifts.length - 2; i >= 0; i--) { + shifts[i] += shifts[i + 1]; + shifts[i] %= 26; + } + + //System.out.println(Arrays.toString(shifts)); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < s.length(); i++) { + int temp = s.charAt(i) - 'a'; + temp = (temp + shifts[i]) % 26; + char val = (char) (temp + 'a'); + sb.append("" + val); + } + + return sb.toString(); + } + + public String shiftingLettersEffective(String S, int[] shifts) { + char[] arr = S.toCharArray(); + int sum = 0; + for (int i = shifts.length - 1; i >= 0; i--) { + int cur = arr[i] - 'a'; + sum += shifts[i] % 26; + cur += sum; + arr[i] = (char) ('a' + cur % 26); + } + + return new String(arr); + } +} diff --git a/src/main/java/strings/stringProblems/ShortDistanceBetweenChars.java b/src/main/java/strings/stringProblems/ShortDistanceBetweenChars.java new file mode 100644 index 0000000..4bf41eb --- /dev/null +++ b/src/main/java/strings/stringProblems/ShortDistanceBetweenChars.java @@ -0,0 +1,31 @@ +package strings.stringProblems; + +/** + * https://leetcode.com/problems/shortest-distance-to-a-character + */ +public class ShortDistanceBetweenChars { + + public int[] shortestToChar(String s, char c) { + + int[] result = new int[s.length()]; + //Before the first C value is reached, there is no index behind it to compute a difference. + //So letting lastSeen = Integer.MAX_VALUE assures that the computed difference on the first pass before the first C value is reached is higher than what would be computed on the way back. + int lastSeen = Integer.MAX_VALUE; + for (int i = 0; i < s.length(); i++) { + if (s.charAt(i) == c) { + lastSeen = i; + } + + result[i] = Math.abs(lastSeen - i); + } + + for (int i = s.length() - 1; i >= 0; i--) { + if (s.charAt(i) == c) { + lastSeen = i; + } + result[i] = Math.min(result[i], Math.abs(lastSeen - i)); + } + + return result; + } +} diff --git a/src/main/java/strings/stringProblems/SlowestKey.java b/src/main/java/strings/stringProblems/SlowestKey.java new file mode 100644 index 0000000..4fda0a7 --- /dev/null +++ b/src/main/java/strings/stringProblems/SlowestKey.java @@ -0,0 +1,27 @@ +package strings.stringProblems; + +/** + * https://leetcode.com/problems/slowest-key/ + */ +public class SlowestKey { + public char slowestKey(int[] releaseTimes, String keysPressed) { + int index = 0; + + int duration = 0; + int maxDuration = releaseTimes[0]; + + for (int i = 1; i < releaseTimes.length; i++) { + duration = releaseTimes[i] - releaseTimes[i - 1]; + + if (duration > maxDuration || (maxDuration == duration && keysPressed.charAt(index) < keysPressed.charAt(i))) { + + maxDuration = duration; + index = i; + + } + + } + + return keysPressed.charAt(index); + } +} diff --git a/src/main/java/strings/stringProblems/ZigZag.java b/src/main/java/strings/stringProblems/ZigZag.java new file mode 100644 index 0000000..4ccea44 --- /dev/null +++ b/src/main/java/strings/stringProblems/ZigZag.java @@ -0,0 +1,35 @@ +package strings.stringProblems; + +import java.util.ArrayList; +import java.util.List; + +public class ZigZag { + + public String convert(String s, int numRows) { + if (numRows == 1 || s.length() <= numRows) { + return s; + } + + List grid = new ArrayList<>(numRows); + for (int i = 0; i < numRows; i++) { + grid.add(new StringBuilder()); + } + + int row = 0; + boolean goingDown = false; + + for (char c : s.toCharArray()) { + grid.get(row).append(c); + if (row == 0 || row == numRows - 1) { + goingDown = !goingDown; + } + row += goingDown ? 1 : -1; + } + + StringBuilder result = new StringBuilder(); + for (StringBuilder sb : grid) { + result.append(sb); + } + return result.toString(); + } +} diff --git a/src/main/java/strings/stringmatching/KMP.java b/src/main/java/strings/stringmatching/KMP.java new file mode 100644 index 0000000..484f254 --- /dev/null +++ b/src/main/java/strings/stringmatching/KMP.java @@ -0,0 +1,60 @@ +package strings.stringmatching; + +import java.util.Arrays; + +/** + * the key to KMP algo is prefix suffix match i.e the longest prefix which is also a suffix (LPS) + * + * for example take a pattern abcdabcd + * + * the prefix = [a,ab,abc,abcd,abcda,abcdab,abcdabc,final string] + * the suffix = [d,cd,bcd,abcd,dabcd,cdabcd,bcdabcd, final string] + * we will not consider final string because that'll be used in last step for final comparison + * + * in the above example index abcd is the longest prefix which is same as suffix. + * + * This LPS tells which index should we move back to in case of pattern match failure + */ +public class KMP { + + public static void main(String[] args) { + String given = "abxabcabcabyasd"; + String pattern = "abcaby"; + System.out.println(strStr(given, pattern)); + } + + + public static int strStr( String haystack, String needle) { + if (needle.isEmpty()) return 0; + int[] lps = computeKMPTable(needle); + int i = 0, j = 0, n = haystack.length(), m = needle.length(); + while (i < n) { + if (haystack.charAt(i) == needle.charAt(j)) { + ++i; ++j; + if (j == m) return i - m; // found solution + } else { + if (j != 0) j = lps[j - 1]; // try match with longest prefix suffix + else i++; // don't match -> go to next character of `haystack` string + } + } + return -1; + } + private static int[] computeKMPTable(String pattern) { + int i = 1, j = 0, n = pattern.length(); + int[] lps = new int[n]; + while (i < n) { + if (pattern.charAt(i) == pattern.charAt(j)) { + lps[i++] = ++j; + } else { + if (j != 0) { + j = lps[j - 1]; // try match with longest prefix suffix + } + else{ + i++; // don't match -> go to next character + } + } + } + return lps; + } + +} diff --git a/src/main/java/trees/AVLTree.java b/src/main/java/trees/AVLTree.java new file mode 100644 index 0000000..09e9059 --- /dev/null +++ b/src/main/java/trees/AVLTree.java @@ -0,0 +1,206 @@ +package trees; + +public class AVLTree> implements Tree { + + private Node root; + + @Override + public Tree insert(T data) { + root = insert(data, root); + return this; + } + + private Node insert(T data, Node node) { + if (node == null) { + return new Node<>(data); + } + if (data.compareTo(node.getData()) < 0) { + node.setLeftChild(insert(data, node.getLeftChild())); + } else if (data.compareTo(node.getData()) > 0) { + node.setRightChild(insert(data, node.getRightChild())); + } else { + return node; + } + updateHeight(node); + return applyRotation(node); + } + + @Override + public void delete(T data) { + root = delete(data, root); + } + + private Node delete(T data, Node node) { + if (node == null) { + return null; + } + if (data.compareTo(node.getData()) < 0) { + node.setLeftChild(delete(data, node.getLeftChild())); + } else if (data.compareTo(node.getData()) > 0) { + node.setRightChild(delete(data, node.getRightChild())); + } else { + // One Child or Leaf Node (no children) + if (node.getLeftChild() == null) { + return node.getRightChild(); + } else if (node.getRightChild() == null) { + return node.getLeftChild(); + } + // Two Children + node.setData(getMax(node.getLeftChild())); + node.setLeftChild(delete(node.getData(), node.getLeftChild())); + } + updateHeight(node); + return applyRotation(node); + } + + @Override + public void traverse() { + traverseInOrder(root); + } + + private void traverseInOrder(Node node) { + if (node != null) { + traverseInOrder(node.getLeftChild()); + System.out.println(node); + traverseInOrder(node.getRightChild()); + } + } + + @Override + public T getMax() { + if (isEmpty()) { + return null; + } + return getMax(root); + } + + private T getMax(Node node) { + if (node.getRightChild() != null) { + return getMax(node.getRightChild()); + } + return node.getData(); + } + + @Override + public T getMin() { + if (isEmpty()) { + return null; + } + return getMin(root); + } + + private T getMin(Node node) { + if (node.getLeftChild() != null) { + return getMin(node.getLeftChild()); + } + return node.getData(); + } + + @Override + public boolean isEmpty() { + return root == null; + } + + /** + * LL Rotation + * It is a type of single rotation that is performed when the tree gets unbalanced, + * upon insertion of a node into the left subtree of the left child of the imbalance node i.e., upon Left-Left (LL) insertion. + * This imbalance indicates that the tree is heavy on the left side. + * Hence, a right rotation is applied, left heaviness imbalance is countered and the tree becomes a balanced tree + * RR Rotation + * It is a type of single rotation that is performed when the tree gets unbalanced, + * upon insertion of a node into the right subtree of the right child of the imbalance node i.e., upon Right-Right (RR) insertion. + * This imbalance indicates that the tree is heavy on the right side. + * Hence, a left rotation is applied, right heaviness imbalance is countered and the tree becomes a balanced tree + * LR Rotation + * It is a type of double rotation that is performed when the tree gets unbalanced, + * upon insertion of a node into the right subtree of the left child of the imbalance node i.e., upon Left-Right (LR) insertion. + * Apply RR Rotation on the left subtree of the imbalanced node as the left child of the imbalanced node is right-heavy. + * This process flips the tree and converts it into a left-skewed tree + * This is now the case of LL rotation and by rotating the tree along the edge of the imbalanced node. + * RL Rotation + * It is similar to LR rotation but it is performed when the tree gets unbalanced, + * upon insertion of a node into the left subtree of the right child of the imbalance node + * Apply LL Rotation on the right subtree of the imbalanced node as the right child of the imbalanced node is left-heavy. + * This process flips the tree and converts it into a right-skewed tree. + * Perform RR Rotation on the imbalanced node to balance the right-skewed tree. + * + * + * @param node + * @return + */ + private Node applyRotation(Node node) { + // left heavy situation + if (height(node.getLeftChild()) - height(node.getRightChild()) > 1) { + if (height(node.getLeftChild().getLeftChild()) - height(node.getLeftChild().getRightChild()) < 0) { + // LR situation + node.setLeftChild(rotateLeft(node.getLeftChild())); + } + // LL situation + return rotateRight(node); + } + if (height(node.getLeftChild()) - height(node.getRightChild()) < -1) { // right heavy situation + if (height(node.getRightChild()) > 0) { + // RL situation + node.setRightChild(rotateRight(node.getRightChild())); + } + // RR situation + return rotateLeft(node); + } + return node; + } + + private Node rotateRight(Node node) { + Node leftNode = node.getLeftChild(); + Node centerNode = leftNode.getRightChild(); + leftNode.setRightChild(node); + node.setLeftChild(centerNode); + updateHeight(node); + updateHeight(leftNode); + return leftNode; + } + + private Node rotateLeft(Node node) { + Node rightNode = node.getRightChild(); + Node centerNode = rightNode.getLeftChild(); + rightNode.setLeftChild(node); + node.setRightChild(centerNode); + updateHeight(node); + updateHeight(rightNode); + return rightNode; + } + + private void updateHeight(Node node) { + int maxHeight = Math.max( + height(node.getLeftChild()), + height(node.getRightChild()) + ); + node.setHeight(maxHeight + 1); + } + + private int height(Node node) { + return node != null ? node.getHeight() : 0; + } + + public static void main(String[] args) { + + Tree avlTree = new AVLTree<>(); + avlTree.insert(10).insert(2).insert(6).insert(8).insert(25).insert(18).insert(35).insert(15).insert(22).insert(42) + .insert(30).insert(40).insert(12).insert(17).insert(19).insert(24).insert(28).insert(33).insert(38); + + avlTree.traverse(); + + System.out.println("Max is: " + avlTree.getMax()); + System.out.println("Min is: " + avlTree.getMin()); + + System.out.println("Deleting 42 from Tree"); + avlTree.delete(42); + + System.out.println("New Max is: " + avlTree.getMax()); + + avlTree.traverse(); + + } + + +} \ No newline at end of file diff --git a/src/main/java/trees/BST/BSTIterator.java b/src/main/java/trees/BST/BSTIterator.java new file mode 100644 index 0000000..cd57c3f --- /dev/null +++ b/src/main/java/trees/BST/BSTIterator.java @@ -0,0 +1,33 @@ +package trees.BST; + +import trees.TreeNode; + +import java.util.ArrayDeque; +import java.util.Deque; + +// https://leetcode.com/problems/binary-search-tree-iterator/ +public class BSTIterator { + Deque stack; + + public BSTIterator(TreeNode root) { + stack = new ArrayDeque<>(); + for (TreeNode node = root; node != null; node = node.left) { + stack.push(node); + } + } + + public int next() { + if (!hasNext()) return -1; + TreeNode curr = stack.pop(); + TreeNode right = curr.right; + while (right != null) { + stack.push(right); + right = right.left; + } + return curr.val; + } + + public boolean hasNext() { + return !stack.isEmpty(); + } +} diff --git a/src/main/java/trees/BST/CeilAndFloor.java b/src/main/java/trees/BST/CeilAndFloor.java new file mode 100644 index 0000000..2af4544 --- /dev/null +++ b/src/main/java/trees/BST/CeilAndFloor.java @@ -0,0 +1,110 @@ +package trees.BST; + +import trees.TreeNode; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +public class CeilAndFloor { + + public int[] ceilAndFloor(TreeNode root, int key) { + int[] result = new int[2]; + ceilAndFloorHelper(root, key, result); + return result; + } + + public void ceilAndFloorHelper(TreeNode root, int key, int[] result) { + if (root == null) return; + + if (root.val == key) { + result[0] = root.val; + result[1] = root.val; + return; + } + + if (root.val > key) { + result[0] = root.val; + ceilAndFloorHelper(root.left, key, result); + } else { + result[1] = root.val; + ceilAndFloorHelper(root.right, key, result); + } + } + + public List> closestNodes(TreeNode root, List queries) { + List> result = new ArrayList<>(); + if (root == null) + return result; + + for (int q : queries) { + int[] ceilAndFloor = new int[2]; + Arrays.fill(ceilAndFloor, -1); + closestNodesHelper(root, q, ceilAndFloor); + result.add(Arrays.asList(ceilAndFloor[0], ceilAndFloor[1])); + } + + return result; + } + + //In the worst case (skewed tree), this can become O(n), where n is the number of nodes. + // or we can use inorder traversal to get the sorted list of nodes and then find the closest nodes + //using binary search + public void closestNodesHelper(TreeNode root, int target, int[] ceilAndFloor) { + if (root == null) + return; + + if (root.val == target) { + ceilAndFloor[0] = root.val; + ceilAndFloor[1] = root.val; + return; + } + + if (root.val > target) { + ceilAndFloor[1] = root.val; + closestNodesHelper(root.left, target, ceilAndFloor); + } else { + ceilAndFloor[0] = root.val;// ceil + + closestNodesHelper(root.right, target, ceilAndFloor); + } + } + + //private List closestBinarySearch(List sortedTree, int target){ + // int min = Integer.MIN_VALUE, max = Integer.MAX_VALUE; + // int left = 0, right = sortedTree.size() - 1; + // + // + // // TreeNode curr = root; + // + // while(left <= right){ + // int mid = left + (right - left) / 2; + // int midVal = sortedTree.get(mid); + // + // if(midVal == target){ + // min = midVal; + // max = midVal; + // break; + // } + // + // // min_t is the largest value in the tree that is smaller than or equal to + // // min_t= Max(Set of values < target) + // // Set of values < target are + // // max_t= Min(Set of values > target) + // + // if(midVal < target){ + // min = Math.max(min, midVal); + // left = mid + 1; + // }else { + // max = Math.min(max, midVal); + // right = mid - 1; + // } + // } + // + // min = (min == Integer.MIN_VALUE) ? -1 : min; + // max = (max == Integer.MAX_VALUE) ? -1 : max; + // + // return List.of(min, max); + // } + +} diff --git a/src/main/java/trees/BST/ConstructBSTFromPreorder.java b/src/main/java/trees/BST/ConstructBSTFromPreorder.java new file mode 100644 index 0000000..bb1acef --- /dev/null +++ b/src/main/java/trees/BST/ConstructBSTFromPreorder.java @@ -0,0 +1,91 @@ +package trees.BST; + +import java.util.ArrayDeque; +import java.util.Deque; + +/** + * https://leetcode.com/problems/construct-binary-search-tree-from-preorder-traversal/ + * Return the root node of a binary search tree that matches the given preorder traversal. + *

+ * (Recall that a binary search tree is a binary tree where for every node, + * any descendant of node.left has a value < node.val, + * and any descendant of node.right has a value > node.val. + * Also recall that a preorder traversal displays the value of the node first, + * then traverses node.left, then traverses node.right.) + */ +public class ConstructBSTFromPreorder { + + public static void main(String[] args) { + int[] arr = {8, 3, 1, 6, 4, 7, 10, 14, 13}; + bstFromPreorder(arr); + } + + public TreeNode bstFromPreorderRecursive(int[] preorder) { + return helper(preorder, 0, preorder.length - 1); + } + + private TreeNode helper(int[] preorder, int start, int end) { + if (start > end) return null; + + TreeNode node = new TreeNode(preorder[start]); + int i; + // 8, 3, 1, 6, 4, 7, 10, 14, 13 + // Root is 8 (index 0) + //The loop finds that 10 (index 6) is the first element greater than 8 + //So, elements from index 1 to 5 form the left subtree, and 6 to 8 form the right subtree + // + //For the left subtree {3, 1, 6, 4, 7}: + //Root is 3 (index 1) + //The loop finds that 6 (index 3) is the first element greater than 3 + //So, {1} forms the left subtree of 3, and {6, 4, 7} forms the right subtree + for (i = start; i <= end; i++) { + if (preorder[i] > node.val) + break; + } + + node.left = helper(preorder, start + 1, i - 1); + node.right = helper(preorder, i, end); + return node; + + + } + + public static TreeNode bstFromPreorder(int[] preorder) { + if (preorder == null || preorder.length == 0) { + return null; + } + Deque stack = new ArrayDeque<>(); + TreeNode root = new TreeNode(preorder[0]); + stack.push(root); + //8, 3, 1, 6, 4, 7, 10, 14, 13 + for (int i = 1; i < preorder.length; i++) { + TreeNode node = new TreeNode(preorder[i]); + if (preorder[i] < stack.peek().val) { + stack.peek().left = node; + } else { + TreeNode parent = stack.peek(); + while (!stack.isEmpty() && preorder[i] > stack.peek().val) { + parent = stack.pop(); + } + parent.right = node; + } + stack.push(node); + } + return root; + } + + static class TreeNode { + TreeNode left; + TreeNode right; + int val; + + public TreeNode(int val) { + this.val = val; + } + + public String toString() { + return val + ""; + } + } +} + diff --git a/src/main/java/trees/BST/DeleteBST.java b/src/main/java/trees/BST/DeleteBST.java new file mode 100644 index 0000000..75346ec --- /dev/null +++ b/src/main/java/trees/BST/DeleteBST.java @@ -0,0 +1,41 @@ +package trees.BST; + +import trees.TreeNode; + +/** + * tricky + * https://leetcode.com/problems/delete-node-in-a-bst/ + * + * if the node has no children, simply remove it by returning null + * if the node has one child, return the child + * if the node has two children, find the inorder successor of the node. + * Copy the value of the inorder successor to the node and then delete the inorder successor by calling step 1. + */ +public class DeleteBST { + public TreeNode deleteNode(TreeNode root, int key) { + if (root == null) return null; + + if (key < root.val) { + root.left = deleteNode(root.left, key); + } else if (key > root.val) { + root.right = deleteNode(root.right, key); + } else { + if (root.left == null && root.right == null) return null; + + else if (root.left == null) return root.right; + else if (root.right == null) return root.left; + else { + root.val = getRightMin(root.right); + root.right = deleteNode(root.right, root.val); + } + } + return root; + } + + public int getRightMin(TreeNode root) { + while (root.left != null) { + root = root.left; + } + return root.val; + } +} diff --git a/src/main/java/trees/BST/GenerateAllPossibleBST.java b/src/main/java/trees/BST/GenerateAllPossibleBST.java new file mode 100644 index 0000000..77e63da --- /dev/null +++ b/src/main/java/trees/BST/GenerateAllPossibleBST.java @@ -0,0 +1,92 @@ +package trees.BST; + +import trees.TreeNode; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * tricky + * + * https://leetcode.com/problems/unique-binary-search-trees-ii/ + * + * 1 1 2 3 3 + * \ \ / \ / / + * 2 3 1 3 2 1 + * \ / / \ + * 3 2 1 2 + * Create root nodes by cycling through [start,end] or [1,n] + * Recursively generate all possible left subtrees. By rules of a BST, all values in a left subtree are less than the root. + * Thus, use the list [start, curRootVal - 1] to create the left subtrees + * Recursively generate all possible right subtrees. By rules of a BST, + * all values in a right subtree are greater than the root. + * Thus, use the list [curRooVal + 1, end] to create the right subtrees. + * For each combination of left and right subtrees, attach them to the current root to form a unique BST (or subtree) + * + * First we want each number for 1..n to be the root, for all different combinations tree roots so we can do + * + * for i in range(1, n+1): + * curRoot = TreeNode(curRootVal) + * we can try to do the same and cycle through [1,n] for each of the children of curRoot + * But we have to obey the rules of a binary search tree which are: + * nodes in a left subtree must be less than the root + * nodes in a right subtree must be greater than the root + * + * So we make each possible left child by cycling through all values less than the current root or [1,curRootVal -1]. + * We make each possible right child by cycling through all values greater than the current root or [curRootVal + 1, n] + * + * Following above method we've created all possible roots using [1,n] in the for loop. + * We somehow made the left subtree using the list [1,curRootVal-1] (numbers less than curRootVal). + * We somehow made the right subtree using the list [curRootVal +1, n] (numbers greater than curRootVal). + * Then we make left_subtree the left child of curRoot and right_subtree the right child of curRoot. + * + * From this code, should the recursive function return a single subtree? + * Take a look at the trees for roots 1 and 3 above, + * they have multiple possible subtrees! Thus, we should return all possible left and right subtrees. + * That is, the recursive function returns a list! I'll call this list all_trees. + */ +public class GenerateAllPossibleBST { + List[][] cache; + + public List generateTrees(int n) { + if (n == 0) return Collections.emptyList(); + cache = new ArrayList[n + 1][n + 1]; + return recursiveTreeBuilder(1, n); + } + + public List recursiveTreeBuilder(int start, int end) { + + List result = new ArrayList<>(); + + // edge case , when i= start the call becomes recursiveTreeBuilder(i, i - 1) + // when i=end the call becomes recursiveTreeBuilder(i+1, i) + // both the cases produce start>end + // so we return null list, the reason is if we return simply and empty list + // for (TreeNode left : leftTree), for (TreeNode right : rightTree) + // one of the for loops for the edge case will get skipped because of empty list + // so we place null at that position + if (start > end) { + result.add(null); + return result; + } + if (cache[start][end] != null) return cache[start][end]; + + for (int i = start; i <= end; i++) { + + List leftTree = recursiveTreeBuilder(start, i - 1); + List rightTree = recursiveTreeBuilder(i + 1, end); + + // the double for loop is to generate all possible combinations of left and right + for (TreeNode left : leftTree) { + for (TreeNode right : rightTree) { + TreeNode root = new TreeNode(i); + root.left = left; + root.right = right; + result.add(root); + } + } + } + return cache[start][end] = result; + } +} diff --git a/src/main/java/trees/BST/InorderSuccessorPredecessor.java b/src/main/java/trees/BST/InorderSuccessorPredecessor.java new file mode 100644 index 0000000..cc03086 --- /dev/null +++ b/src/main/java/trees/BST/InorderSuccessorPredecessor.java @@ -0,0 +1,59 @@ +package trees.BST; + +import trees.TreeNode; + +/** + * https://leetcode.com/problems/inorder-successor-in-bst/ + */ +public class InorderSuccessorPredecessor { + TreeNode result = null; + + public TreeNode inorderSuccessor(TreeNode root, TreeNode p) { + helperFn(root, p); + return result; + } + + public void helperFn(TreeNode root, TreeNode p) { + if (root == null) return; + + if (root.val > p.val) { + result = root; + helperFn(root.left, p); + } else { + helperFn(root.right, p); + } + + } + + // tricky tree traversal + // predecessor go to left first and then go right till end + // so place the result assignment (pre = cur) in the go right block + private TreeNode findPredecessor(TreeNode root, TreeNode p) { + TreeNode predecessor = null; + while (root != null) { + if (root.val >= p.val) { + root = root.left; + } else { + predecessor = root; + root = root.right; + } + } + return predecessor; + } + + // successor go to right first and then go left till end + // so place the result assignment (pre = cur) in the go left block + private TreeNode findSuccessor(TreeNode root, TreeNode p) { + TreeNode successor = null; + while (root != null) { + if (p.val >= root.val) { + root = root.right; + } else { + successor = root; + root = root.left; + } + } + return successor; + } +} + diff --git a/src/main/java/trees/BST/InsertBST.java b/src/main/java/trees/BST/InsertBST.java new file mode 100644 index 0000000..a103d3b --- /dev/null +++ b/src/main/java/trees/BST/InsertBST.java @@ -0,0 +1,57 @@ +package trees.BST; + +import trees.TreeNode; + +// tricky +public class InsertBST { + + public TreeNode insertIntoBST(TreeNode root, int val) { + if (root == null) { + return new TreeNode(val); + } + + if (val < root.val) { + root.left = insertIntoBST(root.left, val); + } else if (val > root.val) { + root.right = insertIntoBST(root.right, val); + } + + return root; + } + + // the solution is simple, just traverse the tree and find the correct place to insert the node + // for example + // 4 + // / \ + // 2 6 + // / \ + // 1 3 + // Inserting 5: + // + //Start at root (4) + //5 > 4, go right to 6 + //5 < 6, try to go left but left is null + //Insert 5 as left child of 6 + public TreeNode insertIntoBSTIterative(TreeNode curr, int val) { + if (curr == null) return new TreeNode(val); + TreeNode root = curr; + while (true) { + // this is a lazy update, the target node is found in one iteration + // and the new node is inserted in the next iteration + if (val < root.val) { + if (root.left == null) { + root.left = new TreeNode(val); + break; + } + root = root.left; + } else { + if (root.right == null) { + root.right = new TreeNode(val); + break; + } + root = root.right; + } + } + return curr; + } +} diff --git a/src/main/java/trees/BST/IsValidBST.java b/src/main/java/trees/BST/IsValidBST.java new file mode 100644 index 0000000..671063f --- /dev/null +++ b/src/main/java/trees/BST/IsValidBST.java @@ -0,0 +1,41 @@ +package trees.BST; + +import trees.TreeNode; + +import java.util.ArrayDeque; +import java.util.Deque; + +/** + * https://leetcode.com/problems/validate-binary-search-tree/ + */ +public class IsValidBST { + + public boolean isValidBST(TreeNode root) { + if (root == null) return true; + Deque stack = new ArrayDeque<>(); + TreeNode pre = null; + while (root != null || !stack.isEmpty()) { + while (root != null) { + stack.push(root); + root = root.left; + } + root = stack.pop(); + if (pre != null && root.val <= pre.val) return false; + pre = root; + root = root.right; + } + return true; + } + + public boolean isValidBSTRecursive(TreeNode root) { + if (root == null) return true; + if (root.right == null && root.left == null) return true; + return isValidBSTHelper(root, Long.MIN_VALUE, Long.MAX_VALUE); + } + + public boolean isValidBSTHelper(TreeNode root, long lowerBound, long upperBound) { + if (root == null) return true; + if (root.val <= lowerBound || root.val >= upperBound) return false; + return isValidBSTHelper(root.left, lowerBound, root.val) && isValidBSTHelper(root.right, root.val, upperBound); + } +} diff --git a/src/main/java/trees/BST/KthSmallestOrLargest.java b/src/main/java/trees/BST/KthSmallestOrLargest.java new file mode 100644 index 0000000..c4e494a --- /dev/null +++ b/src/main/java/trees/BST/KthSmallestOrLargest.java @@ -0,0 +1,46 @@ +package trees.BST; + +import trees.TreeNode; +// https://leetcode.com/problems/kth-smallest-element-in-a-bst/ +public class KthSmallestOrLargest { + + // doing inorder traversal and keeping track of the kth element + public int kthSmallest(TreeNode root, int k) { + int[] result = new int[]{0,0}; + kthSmallestHelper(root,k, result); + return result[1]; + } + + public void kthSmallestHelper(TreeNode root, int k, int[] result){ + if(root==null || result[0]>k) return ; + + kthSmallestHelper(root.left,k,result); + result[0]++; + if (result[0]==k){ + result[1]=root.val; + return; + } + kthSmallestHelper(root.right,k,result); + + } + + // doing reverse inorder traversal and keeping track of the kth element + public int kthLargest(TreeNode root, int k) { + int[] result = new int[]{0,0}; + kthSmallestHelper(root,k, result); + return result[1]; + } + + public void kthLargestHelper(TreeNode root, int k, int[] result){ + if(root==null || result[0]>k) return ; + + kthSmallestHelper(root.right,k,result); + result[0]++; + if (result[0]==k){ + result[1]=root.val; + return; + } + kthSmallestHelper(root.left,k,result); + + } +} diff --git a/src/main/java/trees/BST/LargestBST.java b/src/main/java/trees/BST/LargestBST.java new file mode 100644 index 0000000..5baa329 --- /dev/null +++ b/src/main/java/trees/BST/LargestBST.java @@ -0,0 +1,105 @@ +package trees.BST; + +import trees.TreeNode; + +/** + * tricky BST + */ +public class LargestBST { + public int largestBSTSubtree(TreeNode root) { + if (root == null) return 0; + NodeValue result = largestBSTSubtreeHelper(root); + return result.maxSize; + + } + + + // Idea for a valid BST is if a node root has +// +// root +// / \ +// / \ +// largest in left smallest in right +// +// if root > 'Max left' then it is greater than all the nodes in the left subtree +// if root < 'Min right' then it is smaller than the all the nodes in the right subtree +// +// in case of non bst then pass +INF as left max and -INF as right min which will break the result and then keep the size of the previous result +// +// if root == null pass leftmax as -INF and rightMin as +INF*/ + + // 10 + // / \ + // 5 15 + // / \ / \ + // 1 8 12 20 + // / + // 7 + //We start at the root (10). We recursively call the function on the left and right subtrees. + //Let's follow the left subtree (5) first: + // + //For node 1: It's a leaf, so we return (1, 1, 1) (minNode, maxNode, size) + //For node 7: It's a leaf, so we return (7, 7, 1) + //For node 8: + //Left child (7) < 8 < Right child (null), so it's a BST + //We return (7, 8, 2) + //For node 5: + //1 < 5 < 8, so this subtree is a BST + //We return (1, 8, 4) + // + // + //Now the right subtree (15): + // + //For nodes 12 and 20: They're leaves, so we return (12, 12, 1) and (20, 20, 1) + //For node 15: + //12 < 15 < 20, so this subtree is a BST + //We return (12, 20, 3) + // + // + //Back to the root (10): + // + //Left subtree: (1, 8, 4) + //Right subtree: (12, 20, 3) + //8 < 10 < 12, so the entire tree is a BST + //We return (1, 20, 8) - This is the final result + public NodeValue largestBSTSubtreeHelper(TreeNode root) { + // The reason for this seemingly counterintuitive choice is to ensure + // that these null node values don't interfere with the BST property checks in the parent nodes + //For a null left child, left.maxNode is MIN_VALUE, so root.val is always greater. + //For a null right child, right.minNode is MAX_VALUE, so root.val is always less. + if (root == null) { + return new NodeValue(Integer.MAX_VALUE, Integer.MIN_VALUE, 0); + } + + + //The `NodeValue` information fo the current node is updated + //based on the information from the left and right subtree properties + //ie. the left subtree’s maximum node is less than the current node + //and the right subtree’s minimum node is greater than the current node. + NodeValue left = largestBSTSubtreeHelper(root.left); + NodeValue right = largestBSTSubtreeHelper(root.right); + + int size = Math.max(left.maxSize, right.maxSize); + + if (left.maxNode < root.val && root.val < right.minNode) { + return new NodeValue(Math.min(root.val, left.minNode), Math.max(root.val, right.maxNode), left.maxSize + right.maxSize + 1); + } + //non bst is found, then pass +INF as left max and -INF as right min which will break the result and then keep the size of the previous result + return new NodeValue(Integer.MIN_VALUE, Integer.MAX_VALUE, size); + + + } + + + static class NodeValue { + int maxSize; + int maxNode; + int minNode; + + public NodeValue(int minNode, int maxNode, int maxSize) { + this.minNode = minNode; + this.maxNode = maxNode; + this.maxSize = maxSize; + } + } +} diff --git a/src/main/java/trees/BST/Search.java b/src/main/java/trees/BST/Search.java new file mode 100644 index 0000000..0e81944 --- /dev/null +++ b/src/main/java/trees/BST/Search.java @@ -0,0 +1,23 @@ +package trees.BST; + +import trees.TreeNode; + +// https://leetcode.com/problems/search-in-a-binary-search-tree/ +public class Search { + public TreeNode searchBST(TreeNode root, int val) { + if (root == null) return null; + + if (root.val == val) return root; + + TreeNode result = null; + if (root.val > val) { + result = searchBST(root.left, val); + } + + if (root.val < val) { + result = searchBST(root.right, val); + } + + return result; + } +} diff --git a/src/main/java/trees/BST/SerializeDeserializeBST.java b/src/main/java/trees/BST/SerializeDeserializeBST.java new file mode 100644 index 0000000..2c5e69c --- /dev/null +++ b/src/main/java/trees/BST/SerializeDeserializeBST.java @@ -0,0 +1,60 @@ +package trees.BST; + + +import trees.TreeNode; + +import java.util.LinkedList; +import java.util.Queue; + +class SerializeDeserializeBST { + private static final String NULL_SYMBOL = "X"; + private static final String DELIMITER = ","; + + // Encodes a tree to a single string. + public String serialize(TreeNode root) { + if (root == null) { + return NULL_SYMBOL; + } + + StringBuilder sb = new StringBuilder(); + String left = serialize(root.left); + String right = serialize(root.right); + // root, left, right pre-order traversal + sb.append(root.val).append(DELIMITER).append(left).append(DELIMITER).append(right); + + return sb.toString(); + } + + // Decodes your encoded data to tree. + public TreeNode deserialize(String data) { + if (data.isEmpty()) { + return null; + } + Queue nodes = new LinkedList<>(); + for (String s : data.split(DELIMITER)) { + if (s.equals(NULL_SYMBOL)) + continue; + nodes.offer(Integer.parseInt(s)); + } + return deserializeHelper(nodes); + } + + public TreeNode deserializeHelper(Queue treeValues) { + if (treeValues.isEmpty()) return null; + + Integer rootVal = treeValues.poll(); + TreeNode root = new TreeNode(rootVal); + + Queue smallerValues = new LinkedList<>(); + + while (!treeValues.isEmpty() && treeValues.peek() < rootVal) { + smallerValues.offer(treeValues.poll()); + } + root.left = deserializeHelper(smallerValues); + root.right = deserializeHelper(treeValues); + return root; + + + } + +} \ No newline at end of file diff --git a/src/geeksforgeeks/SortedArrayToBST.java b/src/main/java/trees/BST/SortedArrayToBST.java similarity index 97% rename from src/geeksforgeeks/SortedArrayToBST.java rename to src/main/java/trees/BST/SortedArrayToBST.java index 742aacd..178a094 100644 --- a/src/geeksforgeeks/SortedArrayToBST.java +++ b/src/main/java/trees/BST/SortedArrayToBST.java @@ -1,4 +1,4 @@ -package geeksforgeeks; +package trees.BST; public class SortedArrayToBST { diff --git a/src/main/java/trees/BST/SwapRecoverBST.java b/src/main/java/trees/BST/SwapRecoverBST.java new file mode 100644 index 0000000..8049526 --- /dev/null +++ b/src/main/java/trees/BST/SwapRecoverBST.java @@ -0,0 +1,56 @@ +package trees.BST; + +import trees.TreeNode; + +// https://leetcode.com/problems/recover-binary-search-tree/ +public class SwapRecoverBST { + + TreeNode firstElement = null; + TreeNode secondElement = null; + TreeNode prevElement = null; + + public void recoverTree(TreeNode root) { + + // In order traversal to find the two elements + traverse(root); + // Swap the values of the two nodes + int temp = firstElement.val; + firstElement.val = secondElement.val; + secondElement.val = temp; + } + + private void traverse(TreeNode root) { + + if (root == null) + return; + + traverse(root.left); + // Let's assume this is the original in-order traversal sequence of BST: 1 2 3 4 5 + // If 2 and 3 get swapped, it becomes 1 3 2 4 5 and + //there is only one time that you will have prev.val >= root.val + // If 2 and 4 get swapped, it becomes 1 4 3 2 5 and + //there are two times that you will have prev.val >= root.val + + //When we find the first violation of the BST property (where a node's value is less than or equal to its predecessor), + // we set firstElement to prev. + // This is because in an inorder traversal of a BST, each node should be greater than the previous node + // [1, 6, 3, 4, 5, 2, 7]. + //During inorder traversal: + //When we reach 3, we find that 3 <= 6 is true. This is our first violation. We set firstElement = prev (which is 6). + //We continue until we reach 2. We find that 2 <= 5 is true. + // This is our second violation. We set secondElement = root (which is 2). + if (prevElement != null) { + if (firstElement == null && prevElement.val >= root.val) { + firstElement = prevElement; + } + + //When we find a second violation (or the continuation of the first violation), we set secondElement to the current root. + // This is because this node is smaller than it should be in the BST order. + if (firstElement != null && prevElement.val >= root.val) { + secondElement = root; + } + } + prevElement = root; + traverse(root.right); + } +} \ No newline at end of file diff --git a/src/main/java/trees/BST/TwoSumTree.java b/src/main/java/trees/BST/TwoSumTree.java new file mode 100644 index 0000000..9f4978d --- /dev/null +++ b/src/main/java/trees/BST/TwoSumTree.java @@ -0,0 +1,90 @@ +package trees.BST; + +import trees.TreeNode; + +import java.util.ArrayList; +import java.util.List; +// https://leetcode.com/problems/two-sum-iv-input-is-a-bst/ +public class TwoSumTree { + + public boolean findTargetExtraSpace(TreeNode root, int k) { + if (root == null) return false; + List in = new ArrayList<>(); + + inorder(root, in); + + int i = 0; + int j = in.size() - 1; + + while (i < j) { + if (in.get(i) + in.get(j) == k) + return true; + if (in.get(i) + in.get(j) > k) + j--; + else i++; + } + + return false; + + } + + private void inorder(TreeNode root, List in) { + if (root == null) return; + inorder(root.left, in); + in.add(root.val); + inorder(root.right, in); + } + + public boolean findTarget(TreeNode root, int k) { + if (root == null) { + return false; + } + TreeNode start = root; + TreeNode end = root; + while (start.left != null) { + start = start.left; + } + while (end.right != null) { + end = end.right; + } + while (start != end) { + int sum = start.val + end.val; + if (sum > k) { + end = findPredecessor(root, end); + } else if (sum < k) { + start = findSuccessor(root, start); + } else { + return true; + } + } + return false; + } + + private TreeNode findPredecessor(TreeNode root, TreeNode node) { + TreeNode pre = null; + TreeNode cur = root; + while (cur != null) { + if (cur.val < node.val) { + pre = cur; + cur = cur.right; + } else { + cur = cur.left; + } + } + return pre; + } + + private TreeNode findSuccessor(TreeNode root, TreeNode node) { + TreeNode succ = null; + TreeNode cur = root; + while (cur != null) { + if (cur.val > node.val) { + succ = cur; + cur = cur.left; + } else { + cur = cur.right; + } + } + return succ; + } +} diff --git a/src/main/java/trees/BinaryTreeCamera.java b/src/main/java/trees/BinaryTreeCamera.java new file mode 100644 index 0000000..7ea1931 --- /dev/null +++ b/src/main/java/trees/BinaryTreeCamera.java @@ -0,0 +1,55 @@ +package trees; + + +/** + * https://leetcode.com/problems/binary-tree-cameras/ + */ +public class BinaryTreeCamera { + + int count = 0; + int NOT_MONITERED = 0; + int MONITERED = 1; + int CAMERA = 2; + + public int minCameraCover(TreeNode root) { + if (root == null) return 0; + if (dfs(root) == NOT_MONITERED) count++; + return count; + } + + public int dfs(TreeNode root) { + /** + So that leaf doesn't install camera on it + Note: I will never assign camera to a leaf node. + It will be better if I assign camera to that leaf's + parent node,because parent will cover both its + child as well its own parent as well. + **/ + if (root == null) return MONITERED; + + + int left = dfs(root.left); + int right = dfs(root.right); + + if (left == NOT_MONITERED || right == NOT_MONITERED) { + /** + if any of my child wants me to put a camera on me, + I will have to put a camera on my self. AND I will tell + my parent that I have a camera. Not to worry + */ + count++; + return CAMERA; + } + + if (left == CAMERA || right == CAMERA) { + /** + If any of my child has a camera, I will + be covered as well. So, I will tell my parent + that I am covered + */ + return MONITERED; + } + + return NOT_MONITERED; + } +} diff --git a/src/main/java/trees/BinaryTreesUpsideDown.java b/src/main/java/trees/BinaryTreesUpsideDown.java new file mode 100644 index 0000000..546eed0 --- /dev/null +++ b/src/main/java/trees/BinaryTreesUpsideDown.java @@ -0,0 +1,44 @@ +package trees; + +/** + * https://leetcode.com/problems/binary-tree-upside-down/ + */ +public class BinaryTreesUpsideDown { + /** + * tricky tree swap + */ + public TreeNode upsideDownBinaryTree(TreeNode root) { + TreeNode curr = root; + TreeNode next = null; + TreeNode temp = null; + TreeNode prev = null; + + while (curr != null) { + next = curr.left; + + // swapping nodes now, need temp to keep the previous right child + curr.left = temp; + temp = curr.right; + curr.right = prev; + + prev = curr; + curr = next; + } + return prev; + } + + public TreeNode upsideDownBinaryTreeRecur(TreeNode root) { + if (root == null || root.left == null && root.right == null) + return root; + + TreeNode newRoot = upsideDownBinaryTreeRecur(root.left); + + root.left.left = root.right; + root.left.right = root; + + root.left = null; + root.right = null; + + return newRoot; + } +} diff --git a/src/main/java/trees/BoundaryTraversal.java b/src/main/java/trees/BoundaryTraversal.java new file mode 100644 index 0000000..104714c --- /dev/null +++ b/src/main/java/trees/BoundaryTraversal.java @@ -0,0 +1,66 @@ +package trees; + +import java.util.*; + +public class BoundaryTraversal { + + // the level order traversal is not needed here + // this requires dfs approach + // left side, right side view is totally different from boundary traversal + public List boundaryOfBinaryTree(TreeNode root) { + if (root == null) { + return Collections.emptyList(); + } + List res = new ArrayList<>(); + + // root + if (!isLeaf(root)) { + res.add(root.val); + } + + // left boundary + TreeNode t = root.left; + while (t != null) { + if (!isLeaf(t)) { + res.add(t.val); + } + t = t.left == null ? t.right : t.left; + } + + // leaves + addLeaves(root, res); + + // right boundary(reverse order) + Deque s = new ArrayDeque<>(); + t = root.right; + while (t != null) { + if (!isLeaf(t)) { + s.offer(t.val); + } + t = t.right == null ? t.left : t.right; + } + while (!s.isEmpty()) { + res.add(s.pollLast()); + } + + // output + return res; + } + + private void addLeaves(TreeNode root, List res) { + if (isLeaf(root)) { + res.add(root.val); + return; + } + if (root.left != null) { + addLeaves(root.left, res); + } + if (root.right != null) { + addLeaves(root.right,res); + } + } + + private boolean isLeaf(TreeNode node) { + return node != null && node.left == null && node.right == null; + } +} diff --git a/src/main/java/trees/ClosestValue.java b/src/main/java/trees/ClosestValue.java new file mode 100644 index 0000000..1acf539 --- /dev/null +++ b/src/main/java/trees/ClosestValue.java @@ -0,0 +1,51 @@ +package trees; + +import java.util.ArrayList; +import java.util.Deque; +import java.util.LinkedList; +import java.util.List; + +/** + * https://leetcode.com/problems/closest-binary-search-tree-value + * https://leetcode.com/problems/closest-binary-search-tree-value-ii + */ +public class ClosestValue { + public int closestValue(TreeNode root, double target) { + if (root == null) return 0; + + int val = root.val; + double bestDiff = Math.abs(target - val); + TreeNode node = root; + while (node != null) { + double diff = Math.abs(target - node.val); + if (diff <= bestDiff) { + bestDiff = diff; + val = node.val; + } + node = target <= node.val ? node.left : node.right; + } + + return val; + } + + public List closestKValues(TreeNode root, double target, int k) { + Deque dq = new LinkedList<>(); + + inorder(root, dq); + while (dq.size() > k) { + if (Math.abs(dq.peekFirst() - target) > Math.abs(dq.peekLast() - target)) + dq.pollFirst(); + else + dq.pollLast(); + } + + return new ArrayList<>(dq); + } + + public void inorder(TreeNode root, Deque dq) { + if (root == null) return; + inorder(root.left, dq); + dq.add(root.val); + inorder(root.right, dq); + } +} diff --git a/src/main/java/trees/CompleteBinaryTreeInserter.java b/src/main/java/trees/CompleteBinaryTreeInserter.java new file mode 100644 index 0000000..5e93be6 --- /dev/null +++ b/src/main/java/trees/CompleteBinaryTreeInserter.java @@ -0,0 +1,47 @@ +package trees; + +import java.util.LinkedList; +import java.util.Queue; + +/** + * https://leetcode.com/problems/complete-binary-tree-inserter/ + * + * tricky + */ +public class CompleteBinaryTreeInserter { + private final TreeNode root; + + private final Queue queue; + + public CompleteBinaryTreeInserter(TreeNode root) { + this.root = root; + this.queue = new LinkedList<>(); + queue.add(root); + // iterate till we encounter first non-full node + while (queue.peek()!=null && queue.peek().left != null && queue.peek().right != null) { + TreeNode node = queue.poll(); + queue.offer(node.left); + queue.offer(node.right); + } + if (queue.peek()!=null && queue.peek().left != null) { + queue.offer(queue.peek().left); + } + } + + public int insert(int val) { + TreeNode node = new TreeNode(val); + TreeNode parent = queue.peek(); + if (parent.left == null) { + parent.left = node; + } else if (parent.right == null) { + parent.right = node; + queue.poll(); // remove parent from queue as it is full + } + queue.add(node); + return parent.val; + } + + public TreeNode get_root() { + return root; + } +} diff --git a/src/main/java/trees/ConstructTreeFromInorderAndPostorder.java b/src/main/java/trees/ConstructTreeFromInorderAndPostorder.java new file mode 100644 index 0000000..6f64199 --- /dev/null +++ b/src/main/java/trees/ConstructTreeFromInorderAndPostorder.java @@ -0,0 +1,111 @@ +package trees; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.HashMap; +import java.util.Map; + +//https://leetcode.com/problems/construct-binary-tree-from-inorder-and-postorder-traversal + +public class ConstructTreeFromInorderAndPostorder { + int postorderIndex; + public TreeNode buildTree(int[] inorder, int[] postorder) { + + postorderIndex = postorder.length - 1; + // build a hashmap to store value -> its index relations + Map inorderIndexMap = new HashMap<>(); + for (int i = 0; i < inorder.length; i++) { + inorderIndexMap.put(inorder[i], i); + } + + return arrayToTree(postorder, 0, postorder.length - 1, inorderIndexMap); + } + + private TreeNode arrayToTree(int[] postorder, int left, int right, Map inorderIndexMap) { + // if there are no elements to construct the tree + if (left > right) return null; + + // select the preorder_index element as the root and increment it + int rootValue = postorder[postorderIndex]; + postorderIndex--; + TreeNode root = new TreeNode(rootValue); + + // build left and right subtree + // excluding inorderIndexMap[rootValue] element because it's the root + /* + * The intuition behind it is that since postorder: LEFT → RIGHT → ROOT, + * so when we go in reverse order, we must construct the tree in the order of: ROOT → RIGHT → LEFT + */ + root.right = arrayToTree(postorder, inorderIndexMap.get(rootValue) + 1, right, inorderIndexMap); + root.left = arrayToTree(postorder, left, inorderIndexMap.get(rootValue) - 1, inorderIndexMap); + return root; + } + + public TreeNode buildTreeAlternative(int[] inorder, int[] postorder) { + // build a hashmap to store value -> its index relations + Map inorderIndexMap = new HashMap<>(); + for (int i = 0; i < inorder.length; i++) { + inorderIndexMap.put(inorder[i], i); + } + return helperFn(inorder, 0, inorder.length-1, postorder, 0, postorder.length-1, inorderIndexMap); + } + + public TreeNode helperFn(int[] inorder, int iStart, int iEnd, int[] postorder, int ps, int pe, Map map){ + if(iStart>iEnd || ps>pe) return null; + + TreeNode root= new TreeNode(postorder[pe]); + pe--; + int divider=map.get(root.val); + int leftWindow = divider-iStart; + root.left= helperFn(inorder, iStart,divider-1,postorder,ps,ps+leftWindow-1,map); + root.right=helperFn(inorder,divider+1,iEnd,postorder,ps+leftWindow,pe,map); + return root; + } + + + /** + * The core idea is: Starting from the last element of the postorder and inorder array, + * we put elements from postorder array to a stack and each one is the right child of the last one + * until an element in postorder array is equal to the element on the inorder array. + * Then, we pop as many as elements we can from the stack and decrease the mark in inorder array + * until the peek() element is not equal to the mark value or the stack is empty. + * Then, the new element that we are gonna scan from postorder array is the left child of the last element + * we have popped out from the stack. + * @param inorder + * @param postorder + * @return + */ + public TreeNode buildTreeIterative(int[] inorder, int[] postorder) { + if (inorder.length == 0) return null; + int postOrderPos = postorder.length - 1; + Deque stack = new ArrayDeque<>(); + // last element in the postorder is the root of the tree + TreeNode root = new TreeNode(postorder[postOrderPos--]); + stack.push(root); + TreeNode node = null; + int inorderPos = inorder.length - 1; + + for (; postOrderPos >= 0; postOrderPos--) { + TreeNode cur = new TreeNode(postorder[postOrderPos]); + //pop till all the elements matching inorder elements are removed from stack + while (!stack.isEmpty() && stack.peek().val == inorder[inorderPos]) { + node = stack.pop(); + inorderPos--; + } + if (node != null) { + node.left = cur; + node = null; + } else { + stack.peek().right = cur; + } + stack.push(cur); + } + return root; + } + + public static void main(String[] args) { + ConstructTreeFromInorderAndPostorder tree = new ConstructTreeFromInorderAndPostorder(); + TreeNode root = tree.buildTreeIterative(new int[]{4, 2, 5, 1, 6, 3, 7}, new int[]{4, 5, 2, 6, 7, 3, 1}); + System.out.println(root); + } +} \ No newline at end of file diff --git a/src/main/java/trees/ConstructTreeFromInorderAndPreorder.java b/src/main/java/trees/ConstructTreeFromInorderAndPreorder.java new file mode 100644 index 0000000..4d92986 --- /dev/null +++ b/src/main/java/trees/ConstructTreeFromInorderAndPreorder.java @@ -0,0 +1,94 @@ +package trees; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +/** + * https://leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/ + */ +class ConstructTreeFromInorderAndPreorder { + + // The basic idea is here: + // Say we have 2 arrays, PRE and IN. + // Preorder traversing implies that PRE[0] is the root node. + // Then we can find this PRE[0] in IN, say it's IN[5]. + // Now we know that IN[5] is root, so we know that IN[0] - IN[4] is on the left + // side, IN[6] to the end is on the right side. + // Recursively doing this on sub arrays, we can build a tree out of it :) + int index = 0; + + public TreeNode buildTree(int[] preorder, int[] inorder) { + Map hm = new HashMap<>(); + + if (preorder == null || preorder.length == 0) { + return null; + } + + for (int i = 0; i < inorder.length; i++) { + hm.put(inorder[i], i); + } + + return buildTreeHelper(preorder, 0, inorder.length - 1, hm); + } + + public TreeNode buildTreeHelper(int[] preorder, int left, int right, Map hm) { + if (right < left) { + return null; + } + if (index >= preorder.length) return null; + int currValue = preorder[index++]; + TreeNode node = new TreeNode(currValue); + node.left = buildTreeHelper(preorder, left, hm.get(currValue) - 1, hm); + node.right = buildTreeHelper(preorder, hm.get(currValue) + 1, right, hm); + + /** + * https://takeuforward.org/data-structure/construct-a-binary-tree-from-inorder-and-preorder-traversal/ + * // Create a new TreeNode with value + * // at the current preorder index + * TreeNode root = new TreeNode(preorder.get(preStart)); + * + * // Find the index of the current root + * // value in the inorder traversal + * int inRoot = inMap.get(root.val); + * + * // Calculate the number of + * // elements in the left subtree + * int numsLeft = inRoot - inStart; + * + * // Recursively build the left subtree + * root.left = buildTree(preorder, preStart + 1, preStart + numsLeft, + * inorder, inStart, inRoot - 1, inMap); + * + * // Recursively build the right subtree + * root.right = buildTree(preorder, preStart + numsLeft + 1, preEnd, + * inorder, inRoot + 1, inEnd, inMap); + */ + return node; + } + + public TreeNode buildTreeAlter(int[] preorder, int[] inorder) { + + if(preorder.length == 0 || inorder.length == 0) + return null; + + TreeNode root = new TreeNode(preorder[0]); + + int mid = 0; + int len = inorder.length; + for(int i = 0; i < len; i++) + { + if(inorder[i] == root.val) + { + mid = i; + break; + } + } + //can do copyRange in different lines + root.left = buildTreeAlter(Arrays.copyOfRange(preorder, 1, mid+1), Arrays.copyOfRange(inorder, 0, mid)); + root.right = buildTreeAlter(Arrays.copyOfRange(preorder, mid+1, len), Arrays.copyOfRange(inorder, mid+1,len)); + + return root; + } + +} \ No newline at end of file diff --git a/src/main/java/trees/CountCompleteTreeNodes.java b/src/main/java/trees/CountCompleteTreeNodes.java new file mode 100644 index 0000000..e3456f7 --- /dev/null +++ b/src/main/java/trees/CountCompleteTreeNodes.java @@ -0,0 +1,43 @@ +package trees; + +// https://leetcode.com/problems/count-complete-tree-nodes/ +// idea is to design algorithm that runs in O(log n * log n) time +public class CountCompleteTreeNodes { + + public int countNodes(TreeNode root) { + if (root == null) return 0; + + int leftHeight = findLeftHeight(root); + int rightHeight = findRightHeight(root); + + if (leftHeight == rightHeight) { + return (1 << leftHeight) - 1; + } + + return 1 + countNodes(root.left) + countNodes(root.right); + } + + public int findLeftHeight(TreeNode root) { + if (root == null) return 0; + int height = 0; + + while (root != null) { + height++; + root = root.left; + } + + return height; + } + + public int findRightHeight(TreeNode root) { + if (root == null) return 0; + int height = 0; + + while (root != null) { + height++; + root = root.right; + } + + return height; + } +} diff --git a/src/main/java/trees/CountGoodNodes.java b/src/main/java/trees/CountGoodNodes.java new file mode 100644 index 0000000..6634570 --- /dev/null +++ b/src/main/java/trees/CountGoodNodes.java @@ -0,0 +1,108 @@ +package trees; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; + +/** + * https://leetcode.com/problems/count-good-nodes-in-binary-tree/ + * + * Given a binary tree root, a node X in the tree is named good if in the path from root to X there are no nodes with a value greater than X. + * + * Return the number of good nodes in the binary tree. + */ +public class CountGoodNodes { + + int result = 0; + + /** + * Thought Process: + * + * Question say that, path from root to X there are no nodes with a value greater than X + * In other words, how many elements in the path maintain ascending order ? + * What can be the best way to check if the ascending order is maintained or not ? + * While traversing from root to any node, we can keep a variable having the maximum value till now + * + * Initially let the max value be int_min + * At every node check if ( node value ) >= ( max till now from root ) + * if greater, increase the count( bcz. we got one good node ) and update the max value which we need to pass for left and right subtree + * Now get the count for left and right subtree recursively passing the updated max value as argument + * so final ans will be count( left subtree ) + count( right subtree ) + ( 1 or 0 as per step 2 ) + * + * @param root + * @return + */ + public int goodNodes(TreeNode root) { + if (root == null) return 0; + + return recursionHelper(root, Integer.MIN_VALUE); + } + + + public int recursionHelper(TreeNode root, int max) { + if (root == null) return 0; + + if (root.val >= max) { + max = root.val; + result++; + } + + recursionHelper(root.left, max); + recursionHelper(root.right, max); + return result; + } + + public int goodNodesBruteForce(TreeNode root) { + if (root == null) return 0; + + recursionHelper(root, new ArrayList<>()); + return result; + } + + + public void recursionHelper(TreeNode root, List tempList) { + if (root == null) return; + + tempList.add(root.val); + List temp = new ArrayList<>(tempList); + Collections.sort(temp); + if (root.val >= temp.get(temp.size() - 1)) { + result++; + } + + recursionHelper(root.left, tempList); + recursionHelper(root.right, tempList); + tempList.remove(tempList.size() - 1); + } + + static class Pair{ + TreeNode root; + int val; + Pair(TreeNode root, int val) { + this.root = root; + this.val = val; + } + } + public int goodNodesBFS(TreeNode root) { + if(root == null) return 0; + int count = 0 ; + Queue q = new LinkedList<>(); + q.offer(new Pair(root, root.val)); + while(!q.isEmpty()) { + Pair p = q.poll(); + TreeNode t = p.root; + if(t.val >= p.val) { + count++; + } + if(t.left != null) { + q.offer(new Pair(t.left, Math.max(t.left.val, p.val))); + } + if(t.right != null) { + q.offer(new Pair(t.right, Math.max(t.right.val, p.val))); + } + } + return count; + } +} diff --git a/src/main/java/trees/DepthOfTree.java b/src/main/java/trees/DepthOfTree.java new file mode 100644 index 0000000..96222f4 --- /dev/null +++ b/src/main/java/trees/DepthOfTree.java @@ -0,0 +1,49 @@ +package trees; + +import java.util.ArrayDeque; +import java.util.Deque; + +/** + * https://leetcode.com/problems/maximum-depth-of-binary-tree/ + * https://leetcode.com/problems/minimum-depth-of-binary-tree/ + */ +public class DepthOfTree { + + public int maxDepth(TreeNode root) { + if (root == null) return 0; + return 1 + Math.max(maxDepth(root.left), maxDepth(root.right)); + } + + public int minDepth(TreeNode root) { + + Deque deque = new ArrayDeque<>(); + int result = 0; + deque.offer(root); + + while (!deque.isEmpty()) { + int size = deque.size(); + for (int i = 0; i < size; i++) { + TreeNode temp = deque.poll(); + + if (temp.left == null && temp.right == null) + return result; + if (temp.left != null) { + deque.offer(temp.left); + } + if (temp.right != null) { + deque.offer(temp.right); + } + } + result++; + } + return result; + } + + public int minDepthDFS(TreeNode root) { + if (root == null) return 0; + int left = minDepthDFS(root.left); + int right = minDepthDFS(root.right); + return (left == 0 || right == 0) ? left + right + 1 : Math.min(left, right) + 1; + + } +} diff --git a/src/main/java/trees/DiameterTree.java b/src/main/java/trees/DiameterTree.java new file mode 100644 index 0000000..65d1176 --- /dev/null +++ b/src/main/java/trees/DiameterTree.java @@ -0,0 +1,22 @@ +package trees; + +// https://leetcode.com/problems/diameter-of-binary-tree/ +public class DiameterTree { + int diameter = 0; + public int diameterOfBinaryTree(TreeNode root) { + calculateHeight(root); + return diameter; + } + + public int calculateHeight(TreeNode root) { + if (root == null) return 0; + + int left = calculateHeight(root.left); + int right = calculateHeight(root.right); + + diameter = Math.max(diameter, left + right); + + return 1 + Math.max(left, right); + } + +} diff --git a/src/main/java/trees/FindLeaves.java b/src/main/java/trees/FindLeaves.java new file mode 100644 index 0000000..dcb828e --- /dev/null +++ b/src/main/java/trees/FindLeaves.java @@ -0,0 +1,49 @@ +package trees; + +/** + * https://leetcode.com/problems/find-leaves-of-binary-tree/ + * Given the root of a binary tree, collect a tree's nodes as if you were doing this: + * Collect all the leaf nodes. + * Remove all the leaf nodes. + * Repeat until the tree is empty. + */ + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +public class FindLeaves { + + private int calculateHeight(TreeNode root, List> result) { + if (root == null) return -1; + int height = 1 + Math.max(calculateHeight(root.left, result), calculateHeight(root.right, result)); + if (result.size() <= height) result.add(new HashSet<>()); + result.get(height).add(root); + return height; + } + + + public List> fallingLeaves(TreeNode root) { + List> result = new ArrayList<>(); + calculateHeight(root, result); + return result; + } + + + public int calculateHeightNArray(TreeNode root, List> result) { + if (root == null) return -1; + int max = -1; + + for (TreeNode child : root.children) { + max = Math.max(max, calculateHeight(child, result)); + } + int height = 1 + max; + + if (result.size() <= height) result.add(new HashSet<>()); + result.get(height).add(root); + return height; + } + + +} diff --git a/src/main/java/trees/HouseRobberTree.java b/src/main/java/trees/HouseRobberTree.java new file mode 100644 index 0000000..f06c1e2 --- /dev/null +++ b/src/main/java/trees/HouseRobberTree.java @@ -0,0 +1,64 @@ +package trees; + +/** + * https://leetcode.com/problems/house-robber-iii/ + */ +public class HouseRobberTree { + + /** + * Approach1: Recursive (TLE) + * T.C : O(2^n) + * S.C : O(1) (ignoring stack memory used for recursion) + */ + public int robNaive(TreeNode root) { + if (root == null) return 0; + + int ans = 0; + + // max value from left grandchildren + if (root.left != null) { + ans += robNaive(root.left.left) + robNaive(root.left.right); + } + + // max value from right grandchildren + if (root.right != null) { + ans += robNaive(root.right.left) + robNaive(root.right.right); + } + + return Math.max(ans + root.val, robNaive(root.left) + robNaive(root.right)); //(Case1,Case2) + } + + public int rob(TreeNode root) { + int[] res = robSub(root); + return Math.max(res[0], res[1]); + } + + /** + * Redefine rob(root) as a new function which will return an array of two elements, + * the first element of which denotes the maximum amount of money that can be robbed if root is not robbed, + * while the second element signifies the maximum amount of money robbed if it is robbed. + */ + private int[] robSub(TreeNode root) { + if (root == null) return new int[2]; + + int[] left = robSub(root.left); + int[] right = robSub(root.right); + int[] res = new int[2]; + + /** + * Let's relate rob(root) to rob(root.left) and rob(root.right)..., etc. + * For the 1st element of rob(root), we only need to sum up the larger elements of rob(root.left) and rob(root.right), respectively, + * since root is not robbed and we are free to rob its left and right subtrees + */ + res[0] = Math.max(left[0], left[1]) + Math.max(right[0], right[1]); + + + /** + * For the 2nd element of rob(root), however, we only need to add up the 1st elements of rob(root.left) and rob(root.right), respectively, + * plus the value robbed from root itself, since in this case it's guaranteed that we cannot rob the nodes of root.left and root.right. + */ + res[1] = root.val + left[0] + right[0]; + + return res; + } +} diff --git a/src/main/java/trees/IsHeightBalanced.java b/src/main/java/trees/IsHeightBalanced.java new file mode 100644 index 0000000..338e0da --- /dev/null +++ b/src/main/java/trees/IsHeightBalanced.java @@ -0,0 +1,26 @@ +package trees; + +// https://leetcode.com/problems/balanced-binary-tree/ +public class IsHeightBalanced { + public boolean isBalanced(TreeNode root) { + return isDepthBalanced(root)!=-1; + } + + public int isDepthBalanced(TreeNode root){ + if(root==null) return 0; + + int left = isDepthBalanced(root.left); + if (left==-1){ + return -1; + } + int right = isDepthBalanced(root.right); + if (right==-1){ + return -1; + } + + if (Math.abs(left-right)>1){ + return -1; + } + return 1+Math.max(left,right); + } +} diff --git a/src/main/java/trees/LongestUniValuePath.java b/src/main/java/trees/LongestUniValuePath.java new file mode 100644 index 0000000..893181c --- /dev/null +++ b/src/main/java/trees/LongestUniValuePath.java @@ -0,0 +1,27 @@ +package trees; + +// https://leetcode.com/problems/longest-univalue-path/ +public class LongestUniValuePath { + + Integer result=0; + public int longestUnivaluePath(TreeNode root) { + + if(root==null) return 0; + + helperFn(root,root.val); + + return result; + + } + + private int helperFn(TreeNode root, int prevVal){ + if(root==null) return 0; + + int left=helperFn(root.left, root.val); + int right=helperFn(root.right,root.val); + result= Math.max(result,left+right); + if(root.val==prevVal) return 1+Math.max(left,right); + return 0; + } + +} diff --git a/src/main/java/trees/LowestCommonAncestor.java b/src/main/java/trees/LowestCommonAncestor.java new file mode 100644 index 0000000..e8769da --- /dev/null +++ b/src/main/java/trees/LowestCommonAncestor.java @@ -0,0 +1,171 @@ +package trees; + +import java.util.HashSet; +import java.util.Set; + +//https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-tree/ +//https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/ +public class LowestCommonAncestor { + + public TreeNode lowestCommonAncestorBinarySearchTree(TreeNode root, TreeNode p, TreeNode q) { + while (root != null) { + // 1st one is that if both p & q are smaller than the root then call the left subtree + if (root.val > p.val && root.val > q.val) + root = root.left; + // 2nd if both p & q are greater than the root then call the right subtree + else if (root.val < p.val && root.val < q.val) + root = root.right; + else + return root; + } + // if(root==null) return null; + // + // if(root.val>p.val && root.val>q.val){ + // return lowestCommonAncestor(root.left,p,q); + // }else if(root.val seen = new HashSet<>(); + while (p != null) { + seen.add(p); + p = p.parent; + } + + while (q != null) { + if (seen.contains(q)) { + return q; + } + q = q.parent; + } + + return null; + } + + /** + * tricky LCA + */ + public Node lowestCommonAncestorIII(Node p, Node q) { + Node pRunner = p; + Node qRunner = q; + + while (pRunner != qRunner) { + // Guaranteed to complete since both nodes belong to same tree + // We are simulating a cycle when any of the conditions below is satisfied + // by pointing runner to the head of the other "list" to make sure + // intersection is found before either of the below conditions are satisfied again + pRunner = pRunner.parent == null ? q : pRunner.parent; + qRunner = qRunner.parent == null ? p : qRunner.parent; + } + + return pRunner; + } + + public Node lowestCommonAncestor(Node p, Node q) { + if (p == null || q == null) + throw new IllegalArgumentException("Invalid input as p and q are guaranteed to exist"); + + int pDepth = getDepth(p), qDepth = getDepth(q); + + while (pDepth > qDepth) { + pDepth--; + p = p.parent; + } + + while (qDepth > pDepth) { + qDepth--; + q = q.parent; + } + + while (p != q) { + p = p.parent; + q = q.parent; + } + + return p; + } + + private int getDepth(Node a) { + int depth = 0; + while (a != null) { + a = a.parent; + depth++; + } + return depth; + } + + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode[] nodes) { + Set s = new HashSet<>(); + for (TreeNode n : nodes) s.add(n.val); + return lcaHelper(root, s); + } + + private TreeNode lcaHelper(TreeNode root, Set s) { + if (root == null) return null; + if (s.contains(root.val)) return root; + TreeNode left = lcaHelper(root.left, s); + TreeNode right = lcaHelper(root.right, s); + if (left != null && right != null) return root; + else return (left != null) ? left : right; + } + + static class Node { + public Node left; + public Node right; + public Node parent; + public int data; + } + +} diff --git a/src/main/java/trees/MaxPathSum.java b/src/main/java/trees/MaxPathSum.java new file mode 100644 index 0000000..01760ea --- /dev/null +++ b/src/main/java/trees/MaxPathSum.java @@ -0,0 +1,76 @@ +package trees; + +/** + * https://leetcode.com/problems/binary-tree-maximum-path-sum/discuss/603423/Python-Recursion-stack-thinking-process-diagram + * + * tricky Path sum, observe the Note "Note: the path does not need to pass through the root." + * + * -10 + * / \ + * -2 3 + * / \ / \ + * 4 5 -6 2 + * + * result is 7 (from the path 4 -> -2 -> 5 in the left subtree) + */ + +public class MaxPathSum { + + public int maxPathSum(TreeNode root) { + + if (root == null) return 0; + int[] result = new int[1]; + result[0] = Integer.MIN_VALUE; + maxPathSumUtil(root, result); + return result[0]; + } + + /** + * -10 + * / \ + * 9 20 + * / \ / \ + * -4 3 15 7 + + * For node 9: + * leftRootSum = max(0, -4) = 0 // We use max(0, value) to ignore negative paths + * rightRootSum = max(0, 3) = 3 + * rootPathSum = 0 + 3 + 9 = 12 + * Update result[0] to 12 + * Return 9 + max(0, 3) = 12 to parent + + * For node 20: + + * leftRootSum = max(0, 15) = 15 + * rightRootSum = max(0, 7) = 7 + * rootPathSum = 15 + 7 + 20 = 42 + * Update result[0] to 42 + * Return 20 + max(15, 7) = 35 to parent + + * For the root (-10): + * leftRootSum = max(0, 12) = 12 + * rightRootSum = max(0, 35) = 35 + * rootPathSum = 12 + 35 + (-10) = 37 + * result[0] remains 42 (from step 3) + * Return -10 + max(12, 35) = 25 to parent (which doesn't exist) + */ + + public int maxPathSumUtil(TreeNode root, int[] result) { + + if (root == null) return 0; + //We use max(0, value) when calculating leftRootSum and rightRootSum. + // This ensures we don't include negative paths that would decrease our sum. + int leftRootSum = Math.max(0, maxPathSumUtil(root.left, result)); + int rightRootSum = Math.max(0, maxPathSumUtil(root.right, result)); + //By adding these three components, we get the sum of the path that starts from the left subtree, + // goes through the root, and ends in the right subtree (or vice versa). + //Negative values can be part of the maximum path if they're outweighed by positive values. + // In our example, the -10 at the root is part of the path 9 -> -10 -> 20 -> 15, which sums to 34. + int rootPathSum = rightRootSum + leftRootSum + root.val; + + result[0] = Math.max(rootPathSum, result[0]); + + //This represents the maximum sum of a path from the current node to one of its subtrees. + return root.val + Math.max(leftRootSum, rightRootSum); + } +} diff --git a/src/main/java/trees/MaxProductSplitBinaryTree.java b/src/main/java/trees/MaxProductSplitBinaryTree.java new file mode 100644 index 0000000..91d1286 --- /dev/null +++ b/src/main/java/trees/MaxProductSplitBinaryTree.java @@ -0,0 +1,66 @@ +package trees; + +/** + * https://leetcode.com/problems/maximum-product-of-splitted-binary-tree/submissions/ + * + * I will use an example to explain the logic behind the solution of this exercise. + * Below you see a Binary Tree. + * In red, next to each node, you see the sum of all nodes' values rooted at the subtree. + * This information can be computed by running DFS on the tree (see the code in the end). + * For example, the sum of all nodes rooted at the node with value 2 is: 11= 2 + 5+ 4. + * We can note that the sum of all nodes rooted at the root of the tree is the total sum of all nodes. + * This information is important, and we will use it later. + * 1 (21) + * / \ + * (11) 2 3 (9) + * / \ \ + * 4 5 6 + * + * Suppose we cut the edge connecting the node with value 1 and the node with value 2. + * 1 (10) 21 becomes 10 because of cut + * X \ + * (11) 2 3 (9) + * / \ \ + * 4 5 6 + * + * What is the product of the sum of the subtrees? Well, + * we have already pre-calculated the sum of all nodes in the left subtree (rooted at 2) + * and we can note that the sum of all nodes in the right subtree is the total sum minus the sum of the left subtree. + * We have pre-calculated all the information we need! + * However, we do not know which edge deletion results in the maximum product, so we need to try all the edges. + */ +public class MaxProductSplitBinaryTree { + long max = Long.MIN_VALUE; + long totalSum = 0; + + public int maxProduct(TreeNode root) { + sum(root); + max(root, totalSum); + return (int) (max % (1e9 + 7)); + } + + public void max(TreeNode node, long totalSum) { + if (node == null) return; + if (node.left != null) { + int sum1= (int) (totalSum - node.left.val); + int sum2=node.left.val; + max = Math.max(max, (long) sum1 * sum2); + } + if (node.right != null) { + int sum1= (int) (totalSum - node.right.val); + int sum2=node.right.val; + max = Math.max(max, (long) sum1 * sum2); + } + max(node.left, totalSum); + max(node.right, totalSum); + } + + // use the root value to represent the sum of its subtree + public void sum(TreeNode root) { + if (root == null) return; + totalSum += root.val; + sum(root.left); + sum(root.right); + root.val += (root.left == null ? 0 : root.left.val) + (root.right == null ? 0 : root.right.val); + } +} diff --git a/src/main/java/trees/MaxWidthOfBinaryTree.java b/src/main/java/trees/MaxWidthOfBinaryTree.java new file mode 100644 index 0000000..1a2bba0 --- /dev/null +++ b/src/main/java/trees/MaxWidthOfBinaryTree.java @@ -0,0 +1,94 @@ +package trees; + + +import java.util.*; + +/** + * tricky level order and dfs traversal + */ +//https://leetcode.com/problems/maximum-width-of-binary-tree/ +public class MaxWidthOfBinaryTree { + + //Considering nodes are in an array, + //left child of any node at index i should be at index 2i. + //right child of any node at index i should be ar index 2i+1. + // Each time a node is traversed, the position of the node(as it is in a full binary tree) is stored in the HashMap. + //If the position of the parent node is 'n', then the left child is '2 * n' and the right child is '2 * n + 1'. + //The width of each level is the last node's position in this level subtracts the first node's position in this level plus 1. + public int widthOfBinaryTree(TreeNode root) { + if (root == null) return 0; + int maxWidth = 0; + + Map map = new HashMap<>(); + Queue queue = new LinkedList<>(); + queue.offer(root); + map.put(root, 1); // if we are assigning head as 0 then the formula is left=>2*n+1, right=> 2*n+2 + while (!queue.isEmpty()) { + int width = queue.size(); + int start = 0; + int end = 0; + for (int i = 0; i < width; i++) { + TreeNode temp = queue.poll(); + if (i == 0) start = map.get(temp); + if (i == width - 1) end = map.get(temp); + if (temp.left != null) { + map.put(temp.left, map.get(temp) * 2); + queue.offer(temp.left); + } + if (temp.right != null) { + map.put(temp.right, map.get(temp) * 2 + 1); + queue.offer(temp.right); + } + } + + maxWidth = Math.max(maxWidth, end - start + 1); + } + + return maxWidth; + } + + public int widthOfBinaryTreeDFS(TreeNode root) { + if (root == null) { + return 0; + } + return helper(root, 0, 1, new ArrayList()); + + } + + private int helper(TreeNode root, int depth, int index, List list) { + if (root == null) { + return 0; + } + //add index of leftmost node to list, at depth position in list + //The condition depth == list.size() checks if we're seeing a new depth for the first time. + //We only add to the list when we encounter a new depth, ensuring we always store the leftmost node at each level. + //Depth Tree List + // 0 1 [1] + // / \ + // 1 2 3 [1,2] + // / \ \ + // 2 4 5 6 [1,2,4] + // / + // 3 7 [1,2,4,7] + //When we first reach depth 2 at node 4: + // + //depth = 2 + //list.size() = 2 (contains [1, 2] from previous depths) + //Since depth == list.size(), we add 4's index to the list + //Now list becomes [1, 2, 4] + // + // + //When we reach node 5, also at depth 2: + // + //depth = 2 + //list.size() = 3 (contains [1, 2, 4]) + //Since depth != list.size(), we don't add 5's index + if (depth == list.size()) { + list.add(index); + } + int currWidth = index - list.get(depth) + 1; + int leftWidth = helper(root.left, depth + 1, index * 2, list); + int rightWidth = helper(root.right, depth + 1, index * 2 + 1, list); + return Math.max(currWidth, Math.max(leftWidth, rightWidth)); + } +} \ No newline at end of file diff --git a/src/main/java/trees/MorrisTraversal.java b/src/main/java/trees/MorrisTraversal.java new file mode 100644 index 0000000..0fcc0ee --- /dev/null +++ b/src/main/java/trees/MorrisTraversal.java @@ -0,0 +1,104 @@ +package trees; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +// https://youtu.be/80Zug6D1_r4 +// https://takeuforward.org/data-structure/morris-inorder-traversal-of-a-binary-tree/ +public class MorrisTraversal { + + // Morris Traversal is a way to traverse the tree without using any extra space. + public List inorderTraversal(TreeNode root) { + if (root == null) return Collections.emptyList(); + List result = new ArrayList<>(); + + TreeNode current = root; + while (current != null) { + // Case 1: No Left Child + if (current.left == null) { + result.add(current.val); + current = current.right; + continue; + } + // Case 2: Left Child Exists (Find Predecessor) + TreeNode prev = current.left; + while (prev.right != null && prev.right != current) { + prev = prev.right; + } + // If a left child exists, find the predecessor and decide whether to create or remove a thread. + if (prev.right == null) { + prev.right = current; + current = current.left; + } else { + prev.right = null; + result.add(current.val); + current = current.right; + } + + } + + return result; + } + + public List preOrderTraversal(TreeNode root) { + if (root == null) return Collections.emptyList(); + List result = new ArrayList<>(); + + TreeNode current = root; + while (current != null) { + // Case 1: No Left Child + if (current.left == null) { + result.add(current.val); + current = current.right; + continue; + } + // Case 2: Left Child Exists (Find Predecessor) + TreeNode prev = current.left; + while (prev.right != null && prev.right != current) { + prev = prev.right; + } + // If a left child exists, find the predecessor and decide whether to create or remove a thread. + if (prev.right == null) { + prev.right = current; + result.add(current.val); + current = current.left; + } else { + prev.right = null; + + current = current.right; + } + + } + return result; + } + + + public List postOrderTraversal(TreeNode root) { + if (root == null) return Collections.emptyList(); + List result = new ArrayList<>(); + + TreeNode current = root; + while (current != null) { + if (current.right == null) { + result.add(0, current.val); // Add the current node's value to the beginning of the result list + current = current.left; + continue; + } + TreeNode prev = current.right; + while (prev.left != null && prev.left != current) { + prev = prev.left; + } + if (prev.left == null) { + prev.left = current; + result.add(0, current.val); // Add the current node's value to the beginning of the result list + current = current.right; + } else { + prev.left = null; + current = current.left; + } + + } + return result; + } +} diff --git a/src/main/java/trees/MorrisTraversalTreeToDLL.java b/src/main/java/trees/MorrisTraversalTreeToDLL.java new file mode 100644 index 0000000..902bc4d --- /dev/null +++ b/src/main/java/trees/MorrisTraversalTreeToDLL.java @@ -0,0 +1,62 @@ +package trees; + +import java.util.ArrayDeque; +import java.util.Deque; + +/** + * tricky morris traversal + * https://leetcode.com/problems/flatten-binary-tree-to-linked-list/ + * https://youtu.be/sWf7k1x9XR4 + * https://takeuforward.org/data-structure/flatten-binary-tree-to-linked-list/ + */ +public class MorrisTraversalTreeToDLL { + + public void flattenIterativeExtraSpace(TreeNode root) { + Deque stack = new ArrayDeque<>(); + stack.push(root); + while (!stack.isEmpty()) { + TreeNode curr = stack.pop(); + // remember to push right first + //the goal is to convert the given binary tree into a singly-linked list, + // where the left pointer of each node is set to null, and the right pointer is set to the next node in the list. + if (curr.right != null) { + stack.push(curr.right); + } + if (curr.left != null) { + stack.push(curr.left); + } + + if (!stack.isEmpty()) { + curr.right = stack.peek(); + } + curr.left = null; + } + } + + public void flattenMorris(TreeNode root) { + // Handle the null scenario + if (root == null) { + return; + } + TreeNode node = root; + while (node != null) { + // If the node has a left child + if (node.left != null) { + // Find the predecessor node + TreeNode rightmost = node.left; + while (rightmost.right != null) { + rightmost = rightmost.right; + } + // this is the only change from typical morris traversal + // there we assign rightmost.right = node and proceed to node.left + // here we assign rightmost.right = node.right and proceed to node.right + rightmost.right = node.right; + node.right = node.left; + node.left = null; + } + + // move on to the right side of the tree + node = node.right; + } + } +} diff --git a/src/main/java/trees/Node.java b/src/main/java/trees/Node.java new file mode 100644 index 0000000..c8d8b16 --- /dev/null +++ b/src/main/java/trees/Node.java @@ -0,0 +1,52 @@ +package trees; + +import lombok.Data; +import lombok.NonNull; + +@Data +public class Node> { + + @NonNull + private T data; + + private int height = 1; + + private Node leftChild; + private Node rightChild; + + public Node(@NonNull T data) { + this.data = data; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } + + public int getHeight() { + return height; + } + + public void setHeight(int height) { + this.height = height; + } + + public Node getLeftChild() { + return leftChild; + } + + public void setLeftChild(Node leftChild) { + this.leftChild = leftChild; + } + + public Node getRightChild() { + return rightChild; + } + + public void setRightChild(Node rightChild) { + this.rightChild = rightChild; + } +} \ No newline at end of file diff --git a/src/main/java/trees/NodesAtDistanceK.java b/src/main/java/trees/NodesAtDistanceK.java new file mode 100644 index 0000000..60fe4f2 --- /dev/null +++ b/src/main/java/trees/NodesAtDistanceK.java @@ -0,0 +1,68 @@ +package trees; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.Set; + +/** + * https://leetcode.com/problems/all-nodes-distance-k-in-binary-tree/ + * + * this problem can be asked as Min time taken to burn a binary tree starting from target node + * https://www.geeksforgeeks.org/minimum-time-to-burn-a-tree-starting-from-a-leaf-node/ + */ +public class NodesAtDistanceK { + + public List distanceK(TreeNode root, TreeNode target, int K) { + List res = new ArrayList<>(); + Map parentsMap = new HashMap<>(); + buildParentMap(root, root, parentsMap);//first root is node and second root is its parent considered + Set visited = new HashSet<>(); //keep track of visited nodes + Queue q = new LinkedList<>();//for BFS + q.add(target); //instead of root here we are adding target as we need to find from that node + visited.add(target); //also target node need to be visited first + int level = 0; //as we are using level order traversal k == level then we find all the nodes in queue + while (!q.isEmpty()) { + int size = q.size(); + if (level == K) return buildResult(q);//elements remaining in queue will be nodes at dist + for (int i = 0; i < size; i++) { //k from target + TreeNode t = q.remove(); + if (t.left != null && !visited.contains(t.left)) { + q.add(t.left); + visited.add(t.left); + } + if (t.right != null && !visited.contains(t.right)) { + q.add(t.right); + visited.add(t.right); + } + TreeNode parent = parentsMap.get(t); + if (!visited.contains(parent)) { //if parent is not visited + q.add(parent);//add parent to q + visited.add(parent); //mark it as visited + } + } + level++; //we check level wise and each level covers 1 unit distance + } + return res; + } + + private List buildResult(Queue q) { + List lst = new ArrayList<>(); + while (!q.isEmpty()) { + lst.add(q.remove().val); + } + return lst; + } + + private void buildParentMap(TreeNode root, TreeNode parent, Map parentsMap) { + if (root == null) + return; + parentsMap.put(root, parent); + buildParentMap(root.left, root, parentsMap); //build for left subtree root.left will be child of root + buildParentMap(root.right, root, parentsMap);//build for right subtree + } +} diff --git a/src/main/java/trees/PathSum.java b/src/main/java/trees/PathSum.java new file mode 100644 index 0000000..65f3ef7 --- /dev/null +++ b/src/main/java/trees/PathSum.java @@ -0,0 +1,55 @@ +package trees; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class PathSum { + + + /** + * Given the root of a binary tree and an integer targetSum, + * return true if the tree has a root-to-leaf + * path such that adding up all the values along the path equals targetSum. + *

+ * A leaf is a node with no children. + */ + public boolean hasPathSum(TreeNode root, int targetSum) { + if (root == null) return false; + if (root.left == null && root.right == null && targetSum - root.val == 0) return true; + boolean left = hasPathSum(root.left, targetSum - root.val); + boolean right = hasPathSum(root.right, targetSum - root.val); + + return left || right; + } + + /** + * Given the root of a binary tree and an integer targetSum, + * return all root-to-leaf paths where each path's sum equals targetSum. + *

+ * A leaf is a node with no children. + *

+ * Input: root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22 + * Output: [[5,4,11,2],[5,8,4,5]] + */ + + public List> pathSum(TreeNode root, int targetSum) { + if (root == null) return Collections.emptyList(); + List> result = new ArrayList<>(); + pathSumUtil(root, result, targetSum, new ArrayList<>()); + return result; + } + + public void pathSumUtil(TreeNode root, List> result, int targetSum, List tempList) { + if (root == null) return; + tempList.add(root.val); + if (root.left == null && root.right == null && targetSum - root.val == 0) { + result.add(new ArrayList<>(tempList)); + } + + pathSumUtil(root.left, result, targetSum - root.val, tempList); + pathSumUtil(root.right, result, targetSum - root.val, tempList); + tempList.remove(tempList.size() - 1); + } + +} diff --git a/src/main/java/trees/PathSumIII.java b/src/main/java/trees/PathSumIII.java new file mode 100644 index 0000000..3691d7a --- /dev/null +++ b/src/main/java/trees/PathSumIII.java @@ -0,0 +1,91 @@ +package trees; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * tricky + * You are given a binary tree in which each node contains an integer value. + *

+ * Find the number of paths that sum to a given value. + *

+ * The path does not need to start or end at the root or a leaf, + * but it must go downwards (traveling only from parent nodes to child nodes). + *

+ * The tree has no more than 1,000 nodes and the values are in the range -1,000,000 to 1,000,000. + */ +public class PathSumIII { + Map h = new HashMap<>(); + int count = 0; + + public void preorder(TreeNode node, int currSum, int targetSum) { + if (node == null) + return; + + // current prefix sum + currSum += node.val; + + /** + * situation 1, the tree path with the target sum starts from the root. + * That means the current prefix sum is equal to the target sum curr_sum == k, + * so one should increase the counter by 1: count += 1 + */ + if (currSum == targetSum) + count++; + + /** + * In situation 2, the tree path with the target sum starts somewhere downwards. + * That means we should add to the counter the number of times we have seen the prefix sum curr_sum - target + * so far: count += h[curr_sum - target]. + * + * The logic is simple: the current prefix sum is curr_sum, and some elements before the prefix sum was curr_sum - target. + * All the elements in between sum up to curr_sum - (curr_sum - target) = target. + */ + + count += h.getOrDefault(currSum - targetSum, 0); + + // add the current sum into hashmap + // to use it during the child nodes processing + h.put(currSum, h.getOrDefault(currSum, 0) + 1); + + // process left subtree + preorder(node.left, currSum, targetSum); + // process right subtree + preorder(node.right, currSum, targetSum); + + /** + * Now the current subtree is processed. + * It's time to remove the current prefix sum from the hashmap, + * in order not to blend the parallel subtrees: h[curr_sum] -= 1. + */ + h.put(currSum, h.get(currSum) - 1); + } + + public int pathSumAlter(TreeNode root, int sum) { + preorder(root, 0, sum); + return count; + } + + List list = new ArrayList<>(); + + public int pathSumIII(TreeNode root, int sum) { + if (root == null) return 0; + list.add(root.val); + pathSumIII(root.left, sum); + pathSumIII(root.right, sum); + + int tempSum = 0; + for (int i = list.size() - 1; i >= 0; i--) { // coming in reverse order, inorder to avoid considering head node + tempSum += list.get(i); + if (sum == tempSum) { + count++; + } + } + + list.remove(list.size() - 1); + return count; + } + +} \ No newline at end of file diff --git a/src/main/java/trees/PruneBinaryTree.java b/src/main/java/trees/PruneBinaryTree.java new file mode 100644 index 0000000..78f97fc --- /dev/null +++ b/src/main/java/trees/PruneBinaryTree.java @@ -0,0 +1,28 @@ +package trees; + +/** + * https://leetcode.com/problems/binary-tree-pruning/ + *

+ * Given the root of a binary tree, return the same tree where every subtree (of the given tree) not containing a 1 has been removed. + *

+ * A subtree of a node is node plus every node that is a descendant of node. + */ +public class PruneBinaryTree { + + + public TreeNode pruneTreeEffective(TreeNode root) { + if (root == null) return null; + + root.left = pruneTreeEffective(root.left); + root.right = pruneTreeEffective(root.right); + + if (root.val == 0 && root.left == null && root.right == null) { + root = null; + } + + return root; + } +} + + + diff --git a/src/main/java/trees/RootToLeafPaths.java b/src/main/java/trees/RootToLeafPaths.java new file mode 100644 index 0000000..65ce013 --- /dev/null +++ b/src/main/java/trees/RootToLeafPaths.java @@ -0,0 +1,42 @@ +package trees; + +import java.util.LinkedList; +import java.util.List; + +//https://leetcode.com/problems/binary-tree-paths/ +public class RootToLeafPaths { + public List binaryTreePaths(TreeNode root) { + var result= new LinkedList(); + binaryTreePathsUtil(root, result, ""); + return result; + } + + public void binaryTreePathsUtil(TreeNode root, List result , String temp ){ + if(root==null) return; + if ( root.left==null && root.right==null){ + result.add(temp+root.val); + return; + } + + temp+=(root.val+"->"); + binaryTreePathsUtil(root.left,result,temp); + binaryTreePathsUtil(root.right,result,temp); + + } + + public List returnRootToLeafPaths(TreeNode root, int targetVal){ + List result = new LinkedList<>(); + returnRootToLeafPathsUtil(root,result,targetVal); + return result; + } + + public void returnRootToLeafPathsUtil(TreeNode root, List result, int targetVal){ + if(root==null) return; + result.add(root.val); + if(root.val == targetVal) return; + returnRootToLeafPathsUtil(root.left,result,targetVal); + returnRootToLeafPathsUtil(root.right,result,targetVal); + result.remove(result.size()-1); + } +} + diff --git a/src/main/java/trees/SameTree.java b/src/main/java/trees/SameTree.java new file mode 100644 index 0000000..cb4a42a --- /dev/null +++ b/src/main/java/trees/SameTree.java @@ -0,0 +1,47 @@ +package trees; + +import trees.TreeNode; + +import java.util.LinkedList; +import java.util.Queue; + +/** + * Given two binary trees, write a function to check if they are the same or not. + * Two binary trees are considered the same if they are structurally identical and the nodes have the same value. + * Input: 1 1 + * / \ / \ + * 2 1 1 2 + *

+ * [1,2,1], [1,1,2] + *

+ * Output: false + */ +public class SameTree { + + public boolean isSameTree(TreeNode p, TreeNode q) { + Queue queue = new LinkedList<>(); + queue.add(p); + queue.add(q); + while (!queue.isEmpty()) { + TreeNode f = queue.poll(); + TreeNode s = queue.poll(); + if (f == null && s == null) { + continue; + } else if (f == null || s == null || f.val != s.val) { + return false; + } + queue.add(f.left); + queue.add(s.left); + queue.add(f.right); + queue.add(s.right); + } + return true; + } + + public boolean isSameTreeRecur(TreeNode p, TreeNode q) { + if (p == null || q == null) return p == q; + if (p.val == q.val) return true; + + return isSameTreeRecur(p.left, q.left) && isSameTreeRecur(p.right, q.right); + } +} \ No newline at end of file diff --git a/src/main/java/trees/SecondMinValueInSpecialTree.java b/src/main/java/trees/SecondMinValueInSpecialTree.java new file mode 100644 index 0000000..ea58f6d --- /dev/null +++ b/src/main/java/trees/SecondMinValueInSpecialTree.java @@ -0,0 +1,40 @@ +package trees; + +/** + * https://leetcode.com/problems/second-minimum-node-in-a-binary-tree + */ +public class SecondMinValueInSpecialTree { + + public int findSecondMinimumValue(TreeNode root) { + // tree is not empty + // root != null + + // each node in this tree has exactly two or zero sub-node + if (root.left == null) { + return -1; + } + + int left = -1; + if (root.val != root.left.val) { + // find a value bigger than root.val + left = root.left.val; + } else { + left = findSecondMinimumValue(root.left); + } + + int right = -1; + if (root.val != root.right.val) { + right = root.right.val; + } else { + right = findSecondMinimumValue(root.right); + } + + if (left == -1 || right == -1) { + // the bigger value is left or right, or it not exist + return Math.max(left, right); + } else { + // the min of left and right is what we want + return Math.min(left, right); + } + } +} diff --git a/src/main/java/trees/SerializeAndDeserialize.java b/src/main/java/trees/SerializeAndDeserialize.java new file mode 100644 index 0000000..1c6945e --- /dev/null +++ b/src/main/java/trees/SerializeAndDeserialize.java @@ -0,0 +1,76 @@ +package trees; + +import java.util.Arrays; +import java.util.LinkedList; +import java.util.Queue; + +/** + * https://leetcode.com/problems/serialize-and-deserialize-binary-tree/ + * The video to explain this code is here: https://www.youtube.com/watch?v=suj1ro8TIVY + */ + +public class SerializeAndDeserialize { + + private static final String NULL_SYMBOL = "X"; + private static final String DELIMITER = ","; + + public String serialize(TreeNode root) { + + if (root == null) { + return NULL_SYMBOL; + } + + StringBuilder sb = new StringBuilder(); + String left = serialize(root.left); + String right = serialize(root.right); + // root, left, right pre-order traversal + sb.append(root.val).append(DELIMITER).append(left).append(DELIMITER).append(right); + + return sb.toString(); + } + + public TreeNode deserialize(String data) { + Queue nodesLeftToMaterialize = new LinkedList<>(Arrays.asList(data.split(DELIMITER))); + return deserializeHelper(nodesLeftToMaterialize); + } + + public TreeNode deserializeHelper(Queue nodesLeftToMaterialize) { + + String valueForNode = nodesLeftToMaterialize.poll(); + + if (valueForNode == null || valueForNode.equals(NULL_SYMBOL) || valueForNode.trim().isEmpty()) { + return null; + } + + TreeNode newNode = new TreeNode(Integer.parseInt(valueForNode)); + newNode.left = deserializeHelper(nodesLeftToMaterialize); + newNode.right = deserializeHelper(nodesLeftToMaterialize); + + return newNode; + } + + public static void main(String[] args) { + TreeNode root = new TreeNode(1); + root.left = new TreeNode(2); + root.right = new TreeNode(3); + root.right.left = new TreeNode(4); + root.right.right = new TreeNode(5); + SerializeAndDeserialize sede = new SerializeAndDeserialize(); + String serialize = sede.serialize(root); + System.out.println(serialize); + TreeNode deserialze = sede.deserialize(serialize); + sede.printTree(deserialze); + } + + public void printTree(TreeNode node) { + if (node == null) { + System.out.print("X,"); + return; + } + System.out.print(node.val + ","); + printTree(node.left); + printTree(node.right); + } + +} + diff --git a/src/main/java/trees/SymmetricTree.java b/src/main/java/trees/SymmetricTree.java new file mode 100644 index 0000000..bc0a0c7 --- /dev/null +++ b/src/main/java/trees/SymmetricTree.java @@ -0,0 +1,22 @@ +package trees; + + +class SymmetricTree { + public boolean isSymmetric(TreeNode root) { + if (root == null) { + return true; + } + + return isSymmetricHelp(root.left, root.right); + } + + private boolean isSymmetricHelp(TreeNode left, TreeNode right) { + if (left == null || right == null) { + return left == right; + } + if (left.val != right.val) { + return false; + } + return isSymmetricHelp(left.left, right.right) && isSymmetricHelp(left.right, right.left); + } +} \ No newline at end of file diff --git a/src/main/java/trees/Tree.java b/src/main/java/trees/Tree.java new file mode 100644 index 0000000..6191036 --- /dev/null +++ b/src/main/java/trees/Tree.java @@ -0,0 +1,17 @@ +package trees; + +public interface Tree> { + + Tree insert(T data); + + void delete(T data); + + void traverse(); + + T getMax(); + + T getMin(); + + boolean isEmpty(); + +} \ No newline at end of file diff --git a/src/main/java/trees/TreeNode.java b/src/main/java/trees/TreeNode.java new file mode 100644 index 0000000..ccd4a89 --- /dev/null +++ b/src/main/java/trees/TreeNode.java @@ -0,0 +1,18 @@ +package trees; + +import java.util.Collection; + +public class TreeNode { + public TreeNode left; + public TreeNode right; + public int val; + Collection children; + + public TreeNode(int val) { + this.val = val; + } + + public String toString() { + return val + ""; + } +} \ No newline at end of file diff --git a/src/main/java/trees/TreeTraversals.java b/src/main/java/trees/TreeTraversals.java new file mode 100644 index 0000000..00672d6 --- /dev/null +++ b/src/main/java/trees/TreeTraversals.java @@ -0,0 +1,199 @@ +package trees; + +import java.util.*; + +public class TreeTraversals { + + public int index = 0; + public TreeMap> tm; + List nodes = new ArrayList<>(1000); + + public List preOrderTraversal(TreeNode root) { + if (root == null) return Collections.emptyList(); + // add root to the stack + // while stack is not empty + // pop the element from the stack + // add it to the result + // if right is not null add it to the stack + // if left is not null add it to the stack + // reason to add right first is because stack is LIFO + Deque stack = new ArrayDeque<>(); + stack.push(root); + List result = new ArrayList<>(); + while (!stack.isEmpty()) { + TreeNode temp = stack.pop(); + result.add(temp.val); + if (temp.left != null) stack.push(temp.left); + if (temp.right != null) stack.push(temp.right); + } + + return result; + } + + public List inorderTraversal(TreeNode root) { + if (root == null) return Collections.emptyList(); + + Deque stack = new ArrayDeque<>(); + stack.push(root); + List result = new ArrayList<>(); + + while (root != null || !stack.isEmpty()) { + while (root != null) { + stack.push(root); + root = root.left; + } + root = stack.pop(); + result.add(root.val); + root = root.right; + } + + return result; + } + + /** + * Just like with in-order traversal we go to the left subtree as long as we can. + * At the same time we keep adding the nodes to the stack. + * If we can't (left = null) - we try to go to the right subtree. In order to do that we check the last one we added to the stack. + * If it has a right subtree and we haven't visited it yet then we go there and repeat steps 1 and 2. + * Else we visit the node (also pop out of the stack) 'cause by that time we visited left and right subtrees snd it's time to visit their parent. + * After that we continue the outer loop, peek another node from the stack and repeat 2, 3. + * + * @param root + * @return + */ + public List postorderTraversal(TreeNode root) { + List res = new ArrayList<>(); + Deque stack = new ArrayDeque<>(); + TreeNode cur = root, lastVisited = null; + // 1 + // / \ + // 2 3 + // / \ + // 4 5 + //Initialize: cur = 1, stack = [], res = [] + //Push 1, 2, 4 onto stack. cur = null, stack = [1, 2, 4], res = [] + //Process 4: res = [4], lastVisited = 4, stack = [1, 2] + //Peek at 2, it has right child 5. cur = 5 + //Push 5 onto stack. stack = [1, 2, 5] + //Process 5: res = [4, 5], lastVisited = 5, stack = [1, 2] + //Process 2: res = [4, 5, 2], lastVisited = 2, stack = [1] + //Peek at 1, it has unvisited right child 3. cur = 3 + //Push 3 onto stack. stack = [1, 3] + //Process 3: res = [4, 5, 2, 3], lastVisited = 3, stack = [1] + //Finally, process 1: res = [4, 5, 2, 3, 1], stack = [] + + while (!stack.isEmpty() || cur != null) { + while (cur != null) { + stack.push(cur); + cur = cur.left; + } + TreeNode peek = stack.peek(); + if (peek.right != null && peek.right != lastVisited) { + cur = peek.right; + } else { + res.add(peek.val); + //The lastVisited variable is crucial here. + // It helps us determine whether we've already processed the right child of a node. + // Without it, we might end up in an infinite loop when backtracking from a right child to its parent. + lastVisited = stack.pop(); + } + + } + + return res; + } + + //https://leetcode.com/problems/vertical-order-traversal-of-a-binary-tree + // question asks for result in vertical order with some order preservation, can be ignored + public List> verticalOrder(TreeNode root) { + List> res = new ArrayList<>(); + tm = new TreeMap<>(); + if (root == null) return res; + + Queue q = new LinkedList<>(); + q.offer(new Pair(root, 0)); + + while (!q.isEmpty()) { + Pair cur = q.poll(); + tm.computeIfAbsent(cur.index, k -> new ArrayList<>()).add(cur.node.val); + if (cur.node.left != null) { + q.offer(new Pair(cur.node.left, cur.index - 1)); + } + if (cur.node.right != null) { + q.offer(new Pair(cur.node.right, cur.index + 1)); + } + } + + for (int key : tm.keySet()) { + res.add(tm.get(key)); + } + return res; + } + + public List> AllIterativeTraversalInOneGo(TreeNode root) { + List preOrder = new ArrayList<>(); + List inOrder = new ArrayList<>(); + List postOrder = new ArrayList<>(); + int PREORDER = 0; + int INORDER = 1; + int POSTORDER = 2; + Deque stack = new ArrayDeque<>(); + stack.push(new Pair(root, PREORDER)); + + while (!stack.isEmpty()) { + Pair curr = stack.pop(); + + if (curr.index == PREORDER) { + preOrder.add(curr.node.val); + stack.push(new Pair(curr.node, INORDER)); + if (curr.node.left != null) stack.push(new Pair(curr.node.left, PREORDER)); + } else if (curr.index == INORDER) { + inOrder.add(curr.node.val); + stack.push(new Pair(curr.node, POSTORDER)); + if (curr.node.right != null) stack.push(new Pair(curr.node.right, PREORDER)); + } else { + postOrder.add(curr.node.val); + } + } + + return List.of(preOrder, inOrder, postOrder); + + } + + public List rightSideView(TreeNode root) { + List res = new ArrayList<>(); + if (root == null) { + return res; + } + + Queue q = new LinkedList<>(); + q.offer(root); + + while (!q.isEmpty()) { + int n = q.size(); + for (int i = 0; i < n; i++) { + TreeNode curNode = q.poll(); // for left assign if curNode != null + if (i == n - 1) { + res.add(curNode.val); + } + if (curNode.left != null) { + q.offer(curNode.left); + } + if (curNode.right != null) { + q.offer(curNode.right); + } + } + } + return res; + } + + static class Pair { + TreeNode node; + int index; + + public Pair(TreeNode n, int i) { + node = n; + index = i; + } + } +} diff --git a/src/main/java/trees/TrimBst.java b/src/main/java/trees/TrimBst.java new file mode 100644 index 0000000..18dd9df --- /dev/null +++ b/src/main/java/trees/TrimBst.java @@ -0,0 +1,37 @@ +package trees; + +public class TrimBst { + + public TreeNode trimBST(TreeNode root, int L, int R) { + if (root == null) + return null; + + // If the value of this node is less than L, then the whole left subtree will be smaller, + // so we can discard the left subtree and return the root of the trimmed right sub tree + if (root.val < L) + return trimBST(root.right, L, R); + + // If the value of this node is greater than R, then the whole right subtree will be greater + // so we can discard the right subtree and return the root of the trimmed left sub tree + if (root.val > R) + return trimBST(root.left, L, R); + + // If the value of this node does not need to be deleted, + // we need to pass this recursive call downwards to both sides + root.left = trimBST(root.left, L, R); + root.right = trimBST(root.right, L, R); + + // All the processing of the subtrees done, now return this node + return root; + } + + public TreeNode trimBSTPostOrder(TreeNode root, int L, int R) { + if (root == null) return root; + + root.left = trimBSTPostOrder(root.left, L, R); + root.right = trimBSTPostOrder(root.right, L, R); + if (root.val > R) return root.left; + if (root.val < L) return root.right; + return root; + } +} diff --git a/src/main/java/trees/UniqueTreePath.java b/src/main/java/trees/UniqueTreePath.java new file mode 100644 index 0000000..9e6b31f --- /dev/null +++ b/src/main/java/trees/UniqueTreePath.java @@ -0,0 +1,35 @@ +package trees; + +import java.util.HashSet; + +/** + * Given Binary tree, return the number of nodes on the longest distinct path that starts at the root. + * Grab Codility + * Question states that if the current node val is seen anywhere in the tree, stop proceeding further + */ +public class UniqueTreePath { + + int result = 0; + + public int solution(TreeNode T) { + // write your code in Java SE 8 + HashSet set = new HashSet<>(); + helper(T, set); + return result; + + } + + private void helper(TreeNode root, HashSet set) { + if (root == null) + return; + if (set.contains(root.val)) { + return; + } + set.add(root.val); + result = Math.max(result, set.size()); + helper(root.left, set); + helper(root.right, set); + set.remove(root.val); + + } +} diff --git a/src/main/java/trees/Zigzag.java b/src/main/java/trees/Zigzag.java new file mode 100644 index 0000000..c8be3fc --- /dev/null +++ b/src/main/java/trees/Zigzag.java @@ -0,0 +1,36 @@ +package trees; + +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.List; +import java.util.Queue; + +//https://leetcode.com/problems/binary-tree-zigzag-level-order-traversal/ +public class Zigzag { + + public List> zigzagTraversal(TreeNode root) { + List> result= new ArrayList<>(); + if(root==null) return result; + + Queue queue= new ArrayDeque<>(); + int level=1; + queue.offer(root); + while(!queue.isEmpty()){ + int n= queue.size(); + List tempList= new ArrayList<>(); + for(int i=0;i st = new Stack<>(); + String[] strs = preorder.split(","); + for (String curr : strs) { + if (curr.equals("#")) { + // replace a number node and its left child "#" to a "#" node. + while (!st.isEmpty() && st.peek().equals("#")) { + st.pop(); + if (st.isEmpty()) return false; + st.pop(); + } + } + st.push(curr); + } + return st.size() == 1 && st.peek().equals("#"); + } +}