Problem Challenge 1
Dry-run templates
This is the implementation of the solution provided for Problem Challenge 1:
def search_quadruplets(arr, target):arr.sort()quadruplets = []for i in range(0, len(arr)-3):# skip same element to avoid duplicate quadrupletsif i > 0 and arr[i] == arr[i - 1]:continuefor j in range(i + 1, len(arr)-2):# skip same element to avoid duplicate quadrupletsif j > i + 1 and arr[j] == arr[j - 1]:continuesearch_pairs(arr, target, i, j, quadruplets)return quadrupletsdef search_pairs(arr, target_sum, first, second, quadruplets):left = second + 1right = len(arr) - 1while (left < right):quad_sum = arr[first] + arr[second] + arr[left] + arr[right]if quad_sum == target_sum: # found the quadrupletquadruplets.append([arr[first], arr[second], arr[left], arr[right]])left += 1right -= 1while (left < right and arr[left] == arr[left - 1]):left += 1 # skip same element to avoid duplicate quadrupletswhile (left < right and arr[right] == arr[right + 1]):right -= 1 # skip same element to avoid duplicate quadrupletselif quad_sum < target_sum:left += 1 # we need a pair with a bigger sumelse:right -= 1 # we need a pair with a smaller sumdef main():print(search_quadruplets([4, 1, 2, -1, 1, -3], 1))print(search_quadruplets([2, 0, -1, 1, -2, 2], 2))main()
Students may be encouraged to run through the provided solution with the following sample inputs:
Sample Input #1
Input array: [4, 1, 2, -1, 1, -3]
Target: 1
Sorted array: [-3, -1, 1, 1, 2, 4]
Iteration No. | Line No. | i | j | left | right | quad_sum | quadruplets |
- | 3 | - | - | - | - | - | [] |
1 | 4 | 0 | - | - | - | - | [] |
1 | 8 | 0 | 1 | - | - | - | [] |
1 | 17-18 | 0 | 1 | 2 | 5 | - | [] |
1 | 20 | 0 | 1 | 2 | 5 | 1 | [] |
1 | 22 | 0 | 1 | 2 | 5 | 1 | [[-3, -1, 1, 4]] |
1 | 19 | 0 | 1 | 4 | 4 | 1 | [[-3, -1, 1, 4]] |
1 | 8 | 0 | 2 | - | - | - | [[-3, -1, 1, 4]] |
1 | 17-18 | 0 | 2 | 3 | 5 | - | [[-3, -1, 1, 4]] |
1 | 20 | 0 | 2 | 3 | 5 | 3 | [[-3, -1, 1, 4]] |
1 | 19 | 0 | 2 | 3 | 4 | 3 | [[-3, -1, 1, 4]] |
1 | 20 | 0 | 2 | 3 | 4 | 1 | [[-3, -1, 1, 4]] |
1 | 22 | 0 | 2 | 3 | 4 | 1 | [[-3, -1, 1, 4], [-3, 1, 1, 2]] |
1 | 19 | 0 | 2 | 4 | 3 | 1 | [[-3, -1, 1, 4], [-3, 1, 1, 2]] |
1 | 8 | 0 | 3 | - | - | - | [[-3, -1, 1, 4], [-3, 1, 1, 2]] |
2 | 4 | 1 | - | - | - | - | [[-3, -1, 1, 4], [-3, 1, 1, 2]] |
2 | 8 | 1 | 2 | - | - | - | [[-3, -1, 1, 4], [-3, 1, 1, 2]] |
2 | 17-18 | 1 | 2 | 3 | 5 | - | [[-3, -1, 1, 4], [-3, 1, 1, 2]] |
2 | 20 | 1 | 2 | 3 | 5 | 5 | [[-3, -1, 1, 4], [-3, 1, 1, 2]] |
2 | 19 | 1 | 2 | 3 | 4 | - | [[-3, -1, 1, 4], [-3, 1, 1, 2]] |
2 | 20 | 1 | 2 | 3 | 4 | 3 | [[-3, -1, 1, 4], [-3, 1, 1, 2]] |
2 | 19 | 1 | 2 | 3 | 3 | - | [[-3, -1, 1, 4], [-3, 1, 1, 2]] |
2 | 8 | 1 | 3 | - | - | - | [[-3, -1, 1, 4], [-3, 1, 1, 2]] |
3 | 4 | 2 | - | - | - | - | [[-3, -1, 1, 4], [-3, 1, 1, 2]] |
3 | 8 | 2 | 3 | - | - | - | [[-3, -1, 1, 4], [-3, 1, 1, 2]] |
3 | 17-18 | 2 | 3 | 4 | 5 | - | [[-3, -1, 1, 4], [-3, 1, 1, 2]] |
3 | 20 | 2 | 3 | 4 | 5 | 8 | [[-3, -1, 1, 4], [-3, 1, 1, 2]] |
3 | 19 | 2 | 3 | 4 | 4 | - | [[-3, -1, 1, 4], [-3, 1, 1, 2]] |
Sample Input #2
Input array: [2, 0, -1, 1, -2, 2]
Target: 2
Sorted array: [-2, -1, 0, 1, 2, 2]
Iteration No. | Line No. | i | j | left | right | quad_sum | quadruplets |
- | 3 | - | - | - | - | - | [] |
1 | 4 | 0 | - | - | - | - | [] |
1 | 8 | 0 | 1 | - | - | - | [] |
1 | 17-18 | 0 | 1 | 2 | 5 | - | [] |
1 | 20 | 0 | 1 | 2 | 5 | -1 | [] |
1 | 19 | 0 | 1 | 3 | 5 | - | [] |
1 | 20 | 0 | 1 | 3 | 5 | 0 | [] |
1 | 19 | 0 | 1 | 4 | 5 | - | [] |
1 | 20 | 0 | 1 | 4 | 5 | 1 | [] |
1 | 19 | 0 | 1 | 5 | 5 | - | [] |
1 | 8 | 0 | 2 | - | - | - | [] |
1 | 17-18 | 0 | 2 | 3 | 5 | - | [] |
1 | 20 | 0 | 2 | 3 | 5 | 1 | [] |
1 | 19 | 0 | 2 | 4 | 5 | - | [] |
1 | 20 | 0 | 2 | 4 | 5 | 2 | [] |
1 | 22 | 0 | 2 | 4 | 5 | 2 | [[-2, 0, 2, 2]] |
1 | 19 | 0 | 2 | 5 | 4 | - | [[-2, 0, 2, 2]] |
1 | 8 | 0 | 3 | - | - | - | [[-2, 0, 2, 2]] |
1 | 17-18 | 0 | 3 | 4 | 5 | - | [[-2, 0, 2, 2]] |
1 | 20 | 0 | 3 | 4 | 5 | 3 | [[-2, 0, 2, 2]] |
1 | 19 | 0 | 3 | 4 | 4 | - | [[-2, 0, 2, 2]] |
2 | 4 | 1 | - | - | - | - | [[-2, 0, 2, 2]] |
2 | 8 | 1 | 2 | - | - | - | [[-2, 0, 2, 2]] |
2 | 17-18 | 1 | 2 | 3 | 5 | - | [[-2, 0, 2, 2]] |
2 | 20 | 1 | 2 | 3 | 5 | 2 | [[-2, 0, 2, 2]] |
2 | 22 | 1 | 2 | 3 | 5 | 2 | [[-2, 0, 2, 2], [-1, 0, 1, 2]] |
2 | 19 | 1 | 2 | 4 | 4 | - | [[-2, 0, 2, 2], [-1, 0, 1, 2]] |
2 | 8 | 1 | 3 | - | - | - | [[-2, 0, 2, 2], [-1, 0, 1, 2]] |
2 | 17-18 | 1 | 3 | 4 | 5 | - | [[-2, 0, 2, 2], [-1, 0, 1, 2]] |
2 | 20 | 1 | 3 | 4 | 5 | 4 | [[-2, 0, 2, 2], [-1, 0, 1, 2]] |
2 | 19 | 1 | 3 | 4 | 4 | - | [[-2, 0, 2, 2], [-1, 0, 1, 2]] |
3 | 4 | 2 | - | - | - | - | [[-2, 0, 2, 2], [-1, 0, 1, 2]] |
3 | 8 | 2 | 3 | - | - | - | [[-2, 0, 2, 2], [-1, 0, 1, 2]] |
3 | 17-18 | 2 | 3 | 4 | 5 | - | [[-2, 0, 2, 2], [-1, 0, 1, 2]] |
3 | 20 | 2 | 3 | 4 | 5 | 5 | [[-2, 0, 2, 2], [-1, 0, 1, 2]] |
3 | 19 | 2 | 3 | 4 | 4 | - | [[-2, 0, 2, 2], [-1, 0, 1, 2]] |
Step-by-step solution construction
Let’s first write a function search_pairs
that can search an array for pairs that match a target sum:
def search_pairs_simple(arr, target_sum, start, pairs):left = start + 1right = len(arr) - 1print("\tleft: " + str(left), end=" ")print("\tright: " + str(right), end=" ")while (left < right):pair_sum = arr[left] + arr[right]print("pair_sum: "+str(pair_sum), end=" ")if pair_sum == target_sum: # found the pairpairs.append([arr[left], arr[right]])left += 1right -= 1elif pair_sum < target_sum:left += 1 # we need a pair with a bigger sumelse:right -= 1 # we need a pair with a smaller sumprint("pairs: " + str(pairs), end=" ")print("\n\tleft: " + str(left), end = " ")print("\tright: " + str(right), end=" ")def main():arr1 = [2, 0, -1, 1, -2, 2, 1, 1, 1]target1 = 2pairs = []arr1.sort()print("arr: " + str(arr1) + " target: " + str(target1))search_pairs_simple(arr1, target1, 0, pairs)main()
We can utilize the above search_pairs
method to find the quadruplets we’re looking for. We can set up two outer pointers, i
and j
, such that i
and j
iterate through the array in nested loops. Against each pairing of i
and j
, we can call search_pairs
on the remaining array.
The following is the explanation of the code given below:
- On lines 5-7, we are starting the iteration with two variables
i
andj
- Here,
i
moves from0
tolen(arr)-3
andj
moves fromi+1
tolen(arr)-2
- We have to find a pair against each
i
andj
, hence we callsearch_pairs
search_pairs
iterates through the remaining array to generate the pairs that fulfil the target sum criterion- On lines 19-22, we compute the sum of the quadruplet and compare it with
target
- If the sum matches the
target
, we add the quadruplet to thequadruplets
array - Otherwise, we move left and right pointers forward to generate the next pair
def search_quadruplets(arr, target):arr.sort()# print("sorted array: " + str(arr))quadruplets = []for i in range(0, len(arr)-3):print("\ni: " + str(i), end="")for j in range(i + 1, len(arr)-2):print("\n\tj: " + str(j) + "\tchecking sorted array: " + str(arr))search_pairs(arr, target, i, j, quadruplets)return quadrupletsdef search_pairs(arr, target_sum, first, second, quadruplets):left = second + 1right = len(arr) - 1print("\t\tleft: " + str(left), end=" ")print("\tright: " + str(right), end=" ")while (left < right):quad_sum = arr[first] + arr[second] + arr[left] + arr[right]print("quad_sum: "+str(quad_sum), end=" ")if quad_sum == target_sum: # found the quadrupletquadruplets.append([arr[first], arr[second], arr[left], arr[right]])left += 1right -= 1elif quad_sum < target_sum:left += 1 # we need a pair with a bigger sumelse:right -= 1 # we need a pair with a smaller sumprint("quadruplets: " + str(quadruplets), end=" ")print("\n\t\tleft: " + str(left), end = " ")print("\tright: " + str(right), end=" ")def main():arrs = [[2, 0, -1, 1, -2, 2], [4, 1, 1, -1, -1, -3]]targets = [2, 5]for i in range(len(arrs)):print("arr: " + str(arrs[i]) + " target: " + str(targets[i]))print("\n\nThe quadruplets that sum to " + str(targets[i]) + " are: " + str(search_quadruplets(arrs[i],targets[i])))print(("-"*100) + "\n")main()
The above code works fine but it generates duplicate quadruplets if there are consecutive duplicate values in the given array. For example, the quadruplets produced for the array [4, 1, 1, -1, -1, -3]
with target 5
are [[-1, 1, 1, 4], [-1, 1, 1, 4]]
. We need to add conditional statements to skip any duplicate value while iterating the array.
Following is the explanation of the updated code given below:
- On line 8, we compare the values iterated by the variable
i
andcontinue
if the consecutive two indexes have the same value. - On line 13, we compare the values iterated by the variable
j
andcontinue
if two consecutive indexes have the same value. - On line 34, we compare the value at
left
and the one just before it. If they are the same, we moveleft
forward. We continue in this manner until we find consecutive, non-duplicate values. - On line 37, we compare the value at
right
and the one just after it. If they are the same, we moveright
backwards into the array. Again, we continue doing this until we find consecutive, non-duplicate values.
def search_quadruplets(arr, target):arr.sort()quadruplets = []for i in range(0, len(arr)-3):print("\ni: " + str(i))# skip same element to avoid duplicate quadrupletsif i > 0 and arr[i] == arr[i - 1]:continuefor j in range(i + 1, len(arr)-2):print("\n\tj: " + str(j) + "\tchecking sorted array: " + str(arr))# skip same element to avoid duplicate quadrupletsif j > i + 1 and arr[j] == arr[j - 1]:continuesearch_pairs(arr, target, i, j, quadruplets)return quadrupletsdef search_pairs(arr, target_sum, first, second, quadruplets):left = second + 1right = len(arr) - 1print("\t\tleft: " + str(left), end=" ")print("\tright: " + str(right), end=" ")while (left < right):quad_sum = arr[first] + arr[second] + arr[left] + arr[right]print("quad_sum: "+str(quad_sum), end=" ")if quad_sum == target_sum: # found the quadrupletquadruplets.append([arr[first], arr[second], arr[left], arr[right]])left += 1right -= 1while (left < right and arr[left] == arr[left - 1]):left += 1 # skip same element to avoid duplicate quadrupletswhile (left < right and arr[right] == arr[right + 1]):right -= 1 # skip same element to avoid duplicate quadrupletselif quad_sum < target_sum:left += 1 # we need a pair with a bigger sumelse:right -= 1 # we need a pair with a smaller sumprint("quadruplets: " + str(quadruplets), end=" ")print("\n\t\tleft: " + str(left), end = " ")print("\tright: " + str(right), end=" ")def main():arrs = [[2, 0, -1, 1, -2, 2], [4, 1, 1, -1, -1, -3]]targets = [2, 5]for i in range(len(arrs)):print("arr: " + str(arrs[i]) + " target: " + str(targets[i]))print("\n\nThe quadruplets that sum to " + str(targets[i]) + " are: " + str(search_quadruplets(arrs[i],targets[i])))print(("-"*100) + "\n")main()