|
| 1 | +Sliding Window |
| 2 | +=============== |
| 3 | + |
| 4 | +- Introduction: |
| 5 | +============= |
| 6 | + |
| 7 | + The sliding window technique is a commonly used algorithmic strategy for solving problems that involve arrays or lists. |
| 8 | + This technique is particularly effective when dealing with problems related to subarrays or substrings. It involves |
| 9 | + maintaining a window that slides over the data structure to capture a subset of elements, which is then analyzed to |
| 10 | + solve the problem at hand. |
| 11 | + |
| 12 | +- Here’s a brief overview of how the sliding window technique works: |
| 13 | +==================================================================== |
| 14 | + |
| 15 | + 1. Fixed-size Window: |
| 16 | + - A window of a fixed size moves from the beginning to the end of the array. |
| 17 | + - At each step, the window captures a subset of the array's elements. |
| 18 | + - This technique is used to find the maximum/minimum sum of subarrays of a fixed size, average of subarrays, etc. |
| 19 | + |
| 20 | + 2. Variable-size Window: |
| 21 | + - The window size can change dynamically. |
| 22 | + - This approach is useful when the window size is determined based on a condition that changes as you iterate through the array. |
| 23 | + - It is often used in problems where you need to find the smallest or largest subarray that meets a certain condition, |
| 24 | + such as the sum being greater than a given value, or the number of distinct characters in a substring. |
| 25 | + |
| 26 | +- Examples: |
| 27 | +=========== |
| 28 | + |
| 29 | + 1. Fixed-size Window Example: |
| 30 | + |
| 31 | + - Problem: Find the maximum sum of all subarrays of size `k` in an array. |
| 32 | + - Approach: To find the maximum sum of all subarrays of size `k` in an array using the sliding window technique in Java, |
| 33 | + we can follow the steps outlined below. The sliding window approach will help us achieve an optimal time complexity of O(n). |
| 34 | + |
| 35 | + * Here's a Java implementation along with an explanation of the time and space complexity: |
| 36 | + |
| 37 | + public class SlidingWindowMaxSum { |
| 38 | + // Method to find the maximum sum of subarrays of size k |
| 39 | + public static int maxSumSubarray(int[] arr, int k) { |
| 40 | + int n = arr.length; |
| 41 | + if (n < k) { |
| 42 | + throw new IllegalArgumentException("Array length must be greater than or equal to k"); |
| 43 | + } |
| 44 | + |
| 45 | + // Compute the sum of the first window of size k |
| 46 | + int maxSum = 0; |
| 47 | + for (int i = 0; i < k; i++) { |
| 48 | + maxSum += arr[i]; |
| 49 | + } |
| 50 | + |
| 51 | + // Initialize the current window sum to the maxSum |
| 52 | + int windowSum = maxSum; |
| 53 | + |
| 54 | + // Slide the window from the start to the end of the array |
| 55 | + for (int i = k; i < n; i++) { |
| 56 | + windowSum += arr[i] - arr[i - k]; |
| 57 | + maxSum = Math.max(maxSum, windowSum); |
| 58 | + } |
| 59 | + |
| 60 | + return maxSum; |
| 61 | + } |
| 62 | + |
| 63 | + public static void main(String[] args) { |
| 64 | + int[] arr = {1, 2, 3, 1, 4, 5, 2, 8, 1, 1}; |
| 65 | + int k = 3; |
| 66 | + System.out.println("Maximum sum of subarrays of size " + k + " is " + maxSumSubarray(arr, k)); |
| 67 | + } |
| 68 | + } |
| 69 | + |
| 70 | + * Explanation: |
| 71 | + |
| 72 | + 1. Initial Sum Calculation: We first calculate the sum of the initial window of size `k`. This gives us the initial maximum sum. |
| 73 | + 2. Sliding the Window: We then slide the window across the array. For each new position of the window: |
| 74 | + - Add the element that comes into the window (arr[i]). |
| 75 | + - Subtract the element that goes out of the window (arr[i - k]). |
| 76 | + - Update the maximum sum if the current window sum is greater than the previous maximum sum. |
| 77 | + |
| 78 | + * Time and Space Complexity: |
| 79 | + |
| 80 | + - Time Complexity: |
| 81 | + - Initial Sum Calculation: Calculating the sum of the first window takes O(k) time. |
| 82 | + - Sliding the Window: Sliding the window across the rest of the array takes O(n - k) time. |
| 83 | + - Overall, the time complexity is O(n), where (n) is the number of elements in the array. |
| 84 | + |
| 85 | + - Space Complexity: |
| 86 | + - The algorithm uses a constant amount of extra space for variables (`maxSum`, `windowSum`, etc.), leading to a space |
| 87 | + complexity of O(1). |
| 88 | + |
| 89 | + This implementation efficiently finds the maximum sum of all subarrays of size `k` using the sliding window technique with optimal |
| 90 | + time and space complexity. |
| 91 | + |
| 92 | + |
| 93 | + 2. Variable-size Window Example: |
| 94 | + |
| 95 | + - Problem: Find the smallest subarray with a sum greater than or equal to `S`. |
| 96 | + - Approach: To find the smallest subarray with a sum greater than or equal to `S` in an array using the sliding window technique |
| 97 | + in Java, we can follow a variable-size sliding window approach. This technique ensures that we only traverse the array once, |
| 98 | + achieving an optimal time complexity of O(n). |
| 99 | + |
| 100 | + * Java Implementation: |
| 101 | + |
| 102 | + public class SmallestSubarraySum { |
| 103 | + // Method to find the length of the smallest subarray with sum >= S |
| 104 | + public static int smallestSubarrayWithSum(int[] arr, int S) { |
| 105 | + int n = arr.length; |
| 106 | + int minLength = Integer.MAX_VALUE; |
| 107 | + int currentSum = 0; |
| 108 | + int start = 0; |
| 109 | + |
| 110 | + for (int end = 0; end < n; end++) { |
| 111 | + currentSum += arr[end]; |
| 112 | + |
| 113 | + // Shrink the window as small as possible while the window's sum is larger than or equal to S |
| 114 | + while (currentSum >= S) { |
| 115 | + minLength = Math.min(minLength, end - start + 1); |
| 116 | + currentSum -= arr[start]; |
| 117 | + start++; |
| 118 | + } |
| 119 | + } |
| 120 | + |
| 121 | + // Return 0 if no subarray with sum >= S exists |
| 122 | + return minLength == Integer.MAX_VALUE ? 0 : minLength; |
| 123 | + } |
| 124 | + |
| 125 | + public static void main(String[] args) { |
| 126 | + int[] arr = {2, 1, 5, 2, 3, 2}; |
| 127 | + int S = 7; |
| 128 | + int result = smallestSubarrayWithSum(arr, S); |
| 129 | + System.out.println("The length of the smallest subarray with sum >= " + S + " is " + result); |
| 130 | + } |
| 131 | + } |
| 132 | + |
| 133 | + |
| 134 | + * Explanation: |
| 135 | + |
| 136 | + 1. Initialization: We initialize `minLength` to `Integer.MAX_VALUE` to keep track of the minimum length of the |
| 137 | + subarray found. `currentSum` is initialized to 0 to store the sum of the current window, and `start` is initialized |
| 138 | + to 0 to denote the start of the window. |
| 139 | + |
| 140 | + 2. Expanding the Window: We iterate over the array with the `end` pointer, adding the current element to `currentSum`. |
| 141 | + |
| 142 | + 3. Shrinking the Window: While the `currentSum` is greater than or equal to `S`, we try to shrink the window from the |
| 143 | + left by moving the `start` pointer to the right, updating the `minLength` with the current window size if it's smaller |
| 144 | + than the previously recorded minimum length. We subtract the element at the `start` pointer from `currentSum` before moving |
| 145 | + the `start` pointer to the right. |
| 146 | + |
| 147 | + 4. Check and Return: After the loop, if `minLength` is still `Integer.MAX_VALUE`, it means no valid subarray was found, |
| 148 | + and we return 0. Otherwise, we return `minLength`. |
| 149 | + |
| 150 | + * Time and Space Complexity |
| 151 | + |
| 152 | + - Time Complexity: |
| 153 | + - The time complexity is \(O(n)\) because each element is processed at most twice, once when expanding the window and |
| 154 | + once when shrinking it. |
| 155 | + |
| 156 | + - Space Complexity: |
| 157 | + - The algorithm uses a constant amount of extra space for variables (`minLength`, `currentSum`, `start`, etc.), leading |
| 158 | + to a space complexity of \(O(1)\). |
| 159 | + |
| 160 | + This implementation efficiently finds the length of the smallest subarray with a sum greater than or equal to `S` using the |
| 161 | + sliding window technique with optimal time and space complexity. |
| 162 | + |
| 163 | + * Key Points: |
| 164 | + |
| 165 | + - Efficiency: The sliding window technique often reduces the time complexity of problems that would otherwise require nested loops, |
| 166 | + making it O(n) instead of O(n^2). |
| 167 | + - Applications: It is widely used in string problems, subarray problems, and other scenarios where a contiguous subset of elements |
| 168 | + needs to be considered. |
| 169 | + - Adjustability: The window can be adjusted dynamically, which is useful for problems where the size of the subset isn't fixed. |
| 170 | + |
| 171 | + By understanding and implementing the sliding window technique, you can solve a wide range of problems more efficiently and effectively. |
| 172 | + |
| 173 | +- Sliding Window Example LeetCode Question: 632. Smallest Range Covering Elements from K Lists |
| 174 | +=============================================================================================== |
| 175 | + |
| 176 | + To solve the problem of finding the smallest range that includes at least one number from each of `k` sorted lists, you can use |
| 177 | + a min-heap (priority queue) combined with a sliding window approach. Here’s a step-by-step explanation and Java implementation of |
| 178 | + the algorithm (ChatGPT coded the solution 🤖). |
| 179 | + |
| 180 | + * Algorithm Explanation: |
| 181 | + |
| 182 | + 1. Initialization: |
| 183 | + - Use a min-heap (priority queue) to keep track of the smallest element among the current elements of each list. |
| 184 | + - Track the maximum element among the current elements of each list. |
| 185 | + - Keep pointers to the current index of each list. |
| 186 | + |
| 187 | + 2. Heap Operations: |
| 188 | + - Insert the first element of each list into the min-heap along with its list index. |
| 189 | + - Maintain a variable to track the maximum element in the current window. |
| 190 | + |
| 191 | + 3. Sliding Window: |
| 192 | + - Extract the minimum element from the heap (which is the smallest element in the current window). |
| 193 | + - Calculate the current range as the difference between the maximum element and the minimum element. |
| 194 | + - Update the smallest range if the current range is smaller. |
| 195 | + - Move the pointer of the list from which the minimum element was extracted and add the next element from that list to the heap. |
| 196 | + - Continue this process until one of the lists is exhausted. |
| 197 | + |
| 198 | + 4. Termination: |
| 199 | + - The process terminates when one of the lists is exhausted because you can no longer form a range that includes at |
| 200 | + least one number from each list. |
| 201 | + |
| 202 | + * Time and Space Complexity: |
| 203 | + |
| 204 | + - Time Complexity: |
| 205 | + - Inserting and extracting from the heap takes O(log k) time, and since we perform these operations for each element, the overall |
| 206 | + complexity is O(n log k), where `n` is the total number of elements across all lists, and `k` is the number of lists. |
| 207 | + |
| 208 | + - Space Complexity: |
| 209 | + - The space complexity is O(k) due to the heap which stores one element from each of the `k` lists. |
| 210 | + |
| 211 | + *J ava Implementation: |
| 212 | + |
| 213 | + Here’s the Java code implementing the above approach: |
| 214 | + |
| 215 | + import java.util.*; |
| 216 | + |
| 217 | + public class SmallestRangeFinder { |
| 218 | + |
| 219 | + static class Node { |
| 220 | + int value; |
| 221 | + int listIndex; |
| 222 | + int elementIndex; |
| 223 | + |
| 224 | + Node(int value, int listIndex, int elementIndex) { |
| 225 | + this.value = value; |
| 226 | + this.listIndex = listIndex; |
| 227 | + this.elementIndex = elementIndex; |
| 228 | + } |
| 229 | + } |
| 230 | + |
| 231 | + public static int[] findSmallestRange(List<List<Integer>> lists) { |
| 232 | + PriorityQueue<Node> minHeap = new PriorityQueue<>( |
| 233 | + Comparator.comparingInt(n -> n.value) |
| 234 | + ); |
| 235 | + |
| 236 | + int max = Integer.MIN_VALUE; |
| 237 | + int rangeStart = 0, rangeEnd = Integer.MAX_VALUE; |
| 238 | + |
| 239 | + // Initialize the heap with the first element from each list |
| 240 | + for (int i = 0; i < lists.size(); i++) { |
| 241 | + int value = lists.get(i).get(0); |
| 242 | + minHeap.offer(new Node(value, i, 0)); |
| 243 | + max = Math.max(max, value); |
| 244 | + } |
| 245 | + |
| 246 | + while (true) { |
| 247 | + // Get the minimum element from the heap |
| 248 | + Node minNode = minHeap.poll(); |
| 249 | + int min = minNode.value; |
| 250 | + int listIndex = minNode.listIndex; |
| 251 | + int elementIndex = minNode.elementIndex; |
| 252 | + |
| 253 | + // Update the range if the current range is smaller |
| 254 | + if (max - min < rangeEnd - rangeStart) { |
| 255 | + rangeStart = min; |
| 256 | + rangeEnd = max; |
| 257 | + } |
| 258 | + |
| 259 | + // If we have reached the end of one of the lists, we can't continue |
| 260 | + if (elementIndex + 1 >= lists.get(listIndex).size()) { |
| 261 | + break; |
| 262 | + } |
| 263 | + |
| 264 | + // Move to the next element in the list |
| 265 | + int nextValue = lists.get(listIndex).get(elementIndex + 1); |
| 266 | + minHeap.offer(new Node(nextValue, listIndex, elementIndex + 1)); |
| 267 | + max = Math.max(max, nextValue); |
| 268 | + } |
| 269 | + |
| 270 | + return new int[] {rangeStart, rangeEnd}; |
| 271 | + } |
| 272 | + |
| 273 | + public static void main(String[] args) { |
| 274 | + List<List<Integer>> lists = Arrays.asList( |
| 275 | + Arrays.asList(4, 10, 15, 24, 26), |
| 276 | + Arrays.asList(0, 9, 12, 20), |
| 277 | + Arrays.asList(5, 18, 22, 30) |
| 278 | + ); |
| 279 | + |
| 280 | + int[] result = findSmallestRange(lists); |
| 281 | + System.out.println("Smallest range is [" + result[0] + ", " + result[1] + "]"); |
| 282 | + } |
| 283 | + } |
| 284 | + |
| 285 | + * Explanation of the Code |
| 286 | + |
| 287 | + - Node Class: This helps in storing the value of the element, its list index, and its position within the list. |
| 288 | + - Heap Initialization: The heap is initialized with the first element from each list. |
| 289 | + - Heap Operations: We extract the minimum element, update the range if necessary, and add the next element |
| 290 | + from the same list to the heap. |
| 291 | + - Termination: The loop continues until we exhaust one of the lists, ensuring the smallest range is found. |
| 292 | + |
| 293 | + This approach efficiently finds the smallest range that includes at least one number from each of the `k` sorted lists. |
0 commit comments