Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions Arrays/Problems/Add One To Number.java
Original file line number Diff line number Diff line change
@@ -1,28 +1,72 @@
public class Solution {
/**
* Adds one to a number represented as an ArrayList of integers.
*
* This method takes an ArrayList of integers representing the digits of a number
* and increments the number by one. It handles the carry-over logic when digits
* exceed 9, and manages the case where a new digit needs to be added (e.g., 999 + 1 = 1000).
* Leading zeros are removed from the result before returning.
*
* @param A an ArrayList of integers representing digits of a number (each element should be 0-9)
* @return an ArrayList of integers representing the digits of the incremented number,
* with leading zeros removed
*
* Example:
* Input: [1, 2, 3] (represents 123)
* Output: [1, 2, 4] (represents 124)
*
* Input: [9, 9, 9] (represents 999)
* Output: [1, 0, 0, 0] (represents 1000)
*/
public ArrayList<Integer> plusOne(ArrayList<Integer> A) {
// Step 1: Initialize carry to 1 (we're adding 1) and start from the last digit
int carry = 1;
int idx = A.size() - 1;

// Step 2: Process digits from right to left, handling the carry
while (idx >= 0) {
// Step 2a: Add current digit and carry
int temp = A.get(idx) + carry;

// Step 2b: Determine if there's a carry for the next digit (if sum > 9)
carry = temp > 9 ? 1 : 0;

// Step 2c: Keep only the last digit (0-9)
temp = temp > 9 ? temp % 10 : temp;

// Step 2d: Update the current digit with the new value
A.set(idx, temp);

// Step 2e: Stop if no carry, optimization to avoid unnecessary iterations
if (carry == 0) {
break;
}

// Step 2f: Move to the previous digit
idx--;
}

// Step 3: If there's still a carry after processing all digits,
// it means we need an extra digit at the front (e.g., 999 + 1 = 1000)
if (carry > 0) {
A.add(0, carry);
}

// Step 4: Remove leading zeros from the result
ArrayList<Integer> list = new ArrayList<>();
idx = 0;

// Step 4a: Skip all leading zeros
while (idx < A.size() && A.get(idx) == 0) {
idx++;
}

// Step 4b: Add all remaining digits to the result list
while (idx < A.size()) {
list.add(A.get(idx++));
}

// Step 5: Return the final result
return list;
}
}
Expand Down
57 changes: 46 additions & 11 deletions Backtracking/Examples/Modular Expression.java
Original file line number Diff line number Diff line change
@@ -1,22 +1,57 @@
/**
* Problem: Calculate (a^b) % c using modular exponentiation with recursion.
* Algorithm: Divide-and-conquer using fast exponentiation - reduces repeated calculations.
* Time Complexity: O(log b) due to halving the exponent
* Space Complexity: O(log b) for recursion depth
*/
public class Solution {
/**
* Calculates (a^b) % c using fast modular exponentiation.
* Handles overflow and negative modulo correctly.
*
* @param a the base
* @param b the exponent
* @param c the modulo divisor
* @return (a^b) % c
*
* Example:
* a=2, b=10, c=1000 -> 1024 % 1000 = 24
* a=0, b=5, c=7 -> 0
*/
public int Mod(int a, int b, int c) {
// Step 1: Base case - if a is 0, result is always 0
if(a == 0){
return 0;
}

// Step 2: Base case - if b is 0, result is 1 (a^0 = 1)
if(b == 0){
return 1;
}

long y = 0;
if (b % 2 == 0) {
y = Mod(a, b/2, c);
y = (y * y) % c;
}
else {
y = a % c;
y = (y * Mod(a, b - 1, c)) % c;
}

return (int)((y + c) % c);
// Step 3: Declare result variable
long y = 0;

// Step 4: Use fast exponentiation - divide exponent by 2
if (b % 2 == 0) {
// Step 4a: If b is even, compute (a^(b/2))^2
// This reduces the problem size significantly
y = Mod(a, b / 2, c);

// Step 4b: Square the result and apply modulo
y = (y * y) % c;
}
else {
// Step 4c: If b is odd, use a * a^(b-1)
// First compute a % c to handle large 'a' values
y = a % c;

// Step 4d: Multiply with result of a^(b-1) and apply modulo
y = (y * Mod(a, b - 1, c)) % c;
}

// Step 5: Handle negative modulo by adding c if needed
// This ensures result is always in range [0, c-1]
return (int)((y + c) % c);
}
}
55 changes: 55 additions & 0 deletions Backtracking/Examples/Reverse Link List Recursion_new.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* Problem: Reverse a linked list using recursion.
* Algorithm: Recursive approach - reach end of list, then reverse by modifying links.
* Time Complexity: O(n) where n is the number of nodes
* Space Complexity: O(n) for recursion stack depth
*/

/**
* Definition for singly-linked list.
* class ListNode {
* public int val;
* public ListNode next;
* ListNode(int x) { val = x; next = null; }
* }
*/
public class Solution {
/**
* Reverses a linked list using recursion.
* Example: 1 -> 2 -> 3 becomes 3 -> 2 -> 1
*
* @param A the head of the linked list
* @return the new head of the reversed list
*/
public ListNode reverseList(ListNode A) {
// Step 1: Base case - if list is empty, return null
if (A == null) {
return A;
}

// Step 2: Store next node before we modify A
ListNode rest = A.next;

// Step 3: Base case - if current node is last node, return it
// This becomes the new head of reversed list
if (rest == null) {
return A;
}

// Step 4: Disconnect current node from the rest
// This prevents cycles when we reverse
A.next = null;

// Step 5: Recursively reverse the rest of the list
// rest will be the head of already-reversed sublist
ListNode reverse = reverseList(rest);

// Step 6: Connect the rest of the list back to current node
// After recursion, 'rest' points to what was a middle/end node
// Now we make 'rest' point back to 'A' to reverse the link
rest.next = A;

// Step 7: Return the new head (unchanged in recursion)
return reverse;
}
}
46 changes: 46 additions & 0 deletions Backtracking/Problems/Combination Sum II.java
Original file line number Diff line number Diff line change
@@ -1,25 +1,71 @@
/**
* Problem: Find all unique combinations with target sum from array with duplicates.
* Each number can be used at most once, and result should have no duplicates.
* Algorithm: Backtracking with duplicate skipping and index increment to avoid reuse.
* Time Complexity: O(2^n) in worst case
* Space Complexity: O(n) for recursion depth
*/
public class Solution {
/**
* Finds all unique combinations that sum to target B.
* Each element can be used at most once.
*
* @param a the list of candidate numbers (may contain duplicates)
* @param b the target sum
* @return ArrayList of all unique combinations that sum to B
*
* Example:
* a = [10, 1, 2, 7, 6, 1, 5], b = 8
* Output: [[1,1,6], [1,2,5], [1,7], [2,6]] (one 1 used only once per combo)
*/
public ArrayList<ArrayList<Integer>> combinationSum(ArrayList<Integer> a, int b) {
// Step 1: Sort array to group duplicates and enable easy duplicate skipping
Collections.sort(a);

// Step 2: Create result list
ArrayList<ArrayList<Integer>> ans = new ArrayList<>();

// Step 3: Start backtracking from index 0
helper(a, ans, new ArrayList<>(), b, 0);

// Step 4: Return all valid combinations
return new ArrayList<>(ans);
}

/**
* Backtracking helper to find all combinations.
*
* @param a the sorted candidate array
* @param ans the result list
* @param curr the current combination being built
* @param b the remaining sum needed
* @param idx the starting index (each element used at most once)
*/
private void helper(ArrayList<Integer> a, ArrayList<ArrayList<Integer>> ans, ArrayList<Integer> curr, int b, int idx) {
// Step 1: If remaining sum becomes 0, we found valid combination
if (b == 0) {
ans.add(new ArrayList<>(curr));
}
// Step 2: If remaining sum is negative or no more elements, stop
else if (b < 0 || idx == a.size()) {
return;
}
// Step 3: Try including each element (skip duplicates)
else {
for (int i = idx; i < a.size(); i++) {
// Step 3a: Skip duplicate values at same level
// If current value equals previous and we didn't start with previous, skip
if (i > idx && a.get(i).equals(a.get(i - 1))) {
continue;
}

// Step 3b: Choose - add current element to combination
curr.add(a.get(i));

// Step 3c: Explore - recurse from next index (i+1 ensures each element used once)
helper(a, ans, curr, b - a.get(i), i + 1);

// Step 3d: Un-choose - backtrack
curr.remove(curr.size() - 1);
}
}
Expand Down
46 changes: 45 additions & 1 deletion Backtracking/Problems/Combination Sum.java
Original file line number Diff line number Diff line change
@@ -1,24 +1,68 @@
/**
* Problem: Find all combinations of numbers from array that sum to target.
* Numbers can be reused, and duplicates in result should be avoided.
* Algorithm: Backtracking - explore all possible combinations and track valid ones.
* Time Complexity: O(2^n) in worst case for exploring all combinations
* Space Complexity: O(n) for recursion depth + output space
*/
public class Solution {
/**
* Finds all unique combinations that sum to target B.
*
* @param A the list of candidate numbers (may contain duplicates)
* @param B the target sum
* @return ArrayList of all unique combinations that sum to B
*
* Example:
* A = [3, 6, 2], B = 9
* Output: [[3, 3, 3], [3, 6], [2, 2, 2, ...]] or similar
*/
public ArrayList<ArrayList<Integer>> combinationSum(ArrayList<Integer> A, int B) {
// Step 1: Sort array for consistency and easier duplicate handling
ArrayList<ArrayList<Integer>> ans = new ArrayList<>();
Collections.sort(A);

// Step 2: Start backtracking from index 0
// Current combination, remaining sum, and starting index
helper(A, ans, new ArrayList<>(), B, 0);

// Step 3: Return all valid combinations found
return new ArrayList<>(ans);
}

/**
* Backtracking helper to explore all combinations.
*
* @param a the candidate numbers array
* @param ans the result list of all valid combinations
* @param curr the current combination being built
* @param b the remaining sum needed
* @param idx the starting index to avoid duplicates
*/
private void helper(ArrayList<Integer> a, ArrayList<ArrayList<Integer>> ans, ArrayList<Integer> curr, int b, int idx) {
// Step 1: If remaining sum becomes negative, stop this path
if (b < 0) {
return;
}

// Step 2: If remaining sum becomes 0, we found a valid combination
if (b == 0) {
if(!ans.contains(curr)) {
// Add only if this combination is not already in the result
if (!ans.contains(curr)) {
ans.add(new ArrayList<>(curr));
}
return;
}

// Step 3: Try adding each number starting from idx (allows reuse)
for (int i = idx; i < a.size(); i++) {
// Step 3a: Choose - add current number to combination
curr.add(a.get(i));

// Step 3b: Explore - recurse with same index (allows reuse) and reduced sum
helper(a, ans, curr, b - a.get(i), i);

// Step 3c: Un-choose - backtrack by removing the added number
curr.remove(curr.size() - 1);
}
}
Expand Down
Loading