Find all Duplicate Numbers (easy)
Dry-run templates
This is the implementation of the solution provided for the problem posed in the Find all Duplicate Numbers (easy) lesson:
def find_all_duplicates(nums):i = 0while i < len(nums):j = nums[i] - 1if nums[i] != nums[j]:nums[i], nums[j] = nums[j], nums[i] # swapelse:i += 1duplicateNumbers = []for i in range(len(nums)):if nums[i] != i + 1:duplicateNumbers.append(nums[i])return duplicateNumbersdef main():print(find_all_duplicates([3, 4, 4, 5, 5]))print(find_all_duplicates([5, 4, 7, 2, 3, 5, 3]))main()
Students may be encouraged to run through the provided solution with the following sample inputs:
Sample Input #1
nums: [1,4,4,3,2,5,6,6,7]
Iteration No. | Line No. | i | j | nums[i] | nums[j] | nums | duplicateNumbers |
- | 2 | 0 | - | - | - | [1,4,4,3,2,5,6,6,7] | - |
1 | 5 | 0 | 0 | 1 | 1 | [1,4,4,3,2,5,6,6,7] | - |
2 | 5 | 1 | 3 | 4 | 3 | [1,4,4,3,2,5,6,6,7] | - |
2 | 6 | 1 | 3 | 4 | 3 | [1, 3, 4, 4, 2, 5, 6, 6, 7] | - |
3 | 5 | 1 | 2 | 3 | 4 | [1, 3, 4, 4, 2, 5, 6, 6, 7] | - |
3 | 6 | 1 | 2 | 3 | 4 | [1, 4, 3, 4, 2, 5, 6, 6, 7] | - |
4 | 5 | 1 | 3 | 4 | 4 | [1, 4, 3, 4, 2, 5, 6, 6, 7] | - |
5 | 5 | 2 | 2 | 3 | 3 | [1, 4, 3, 4, 2, 5, 6, 6, 7] | - |
6 | 5 | 3 | 3 | 4 | 4 | [1, 4, 3, 4, 2, 5, 6, 6, 7] | - |
7 | 5 | 4 | 1 | 2 | 4 | [1, 4, 3, 4, 2, 5, 6, 6, 7] | - |
7 | 6 | 4 | 1 | 2 | 4 | [1, 2, 3, 4, 4, 5, 6, 6, 7] | |
8 | 5 | 4 | 3 | 4 | 4 | [1, 2, 3, 4, 4, 5, 6, 6, 7] | - |
9 | 5 | 5 | 4 | 5 | 4 | [1, 2, 3, 4, 4, 5, 6, 6, 7] | - |
9 | 6 | 5 | 4 | 5 | 4 | [1, 2, 3, 4, 5, 4, 6, 6, 7] | - |
10 | 5 | 5 | 3 | 4 | 4 | [1, 2, 3, 4, 5, 4, 6, 6, 7] | - |
11 | 5 | 6 | 5 | 6 | 4 | [1, 2, 3, 4, 5, 4, 6, 6, 7] | - |
11 | 6 | 6 | 5 | 6 | 4 | [1, 2, 3, 4, 5, 6, 4, 6, 7] | |
12 | 5 | 6 | 3 | 4 | 4 | [1, 2, 3, 4, 5, 6, 4, 6, 7] | - |
13 | 5 | 7 | 5 | 6 | 6 | [1, 2, 3, 4, 5, 6, 4, 6, 7] | - |
14 | 5 | 8 | 6 | 7 | 4 | [1, 2, 3, 4, 5, 6, 4, 6, 7] | - |
14 | 6 | 8 | 6 | 7 | 4 | [1, 2, 3, 4, 5, 6, 7, 6, 4] | - |
15 | 5 | 8 | 3 | 4 | 4 | [1, 2, 3, 4, 5, 6, 7, 6, 4] | - |
1 | 13 | 8 | - | 6 | - | [1, 2, 3, 4, 5, 6, 7, 6, 4] | [6] |
2 | 13 | 9 | - | 4 | - | [1, 2, 3, 4, 5, 6, 7, 6, 4] | [6,4] |
Sample Input #2
Students may be encouraged to complete the dry-run with this input:
nums: [1,1,2,2,3,3]
Iteration No. | Line No. | i | j | nums[i] | num[j] | nums | duplicateNumbers |
- | 2 | 0 | - | - | - | [1,1,2,2,3,3] | - |
1 | 5 | 0 | 0 | 1 | 1 | [1,1,2,2,3,3] | - |
2 | 5 | 1 | 0 | 1 | 1 | [1,1,2,2,3,3] | - |
3 | 5 | 2 | 1 | 2 | 1 | [1,1,2,2,3,3] | - |
3 | 5 | 2 | 1 | 2 | 1 | [1, 2, 1, 2, 3, 3] | - |
... | ... | ... | ... | ... | ... | .... | ... |
Step-by-step solution construction
The solution requires two steps:
- Cyclic sort (lines 3-15): Place the first occurrence of each number
n
innums
at the indexn-1
(lines 9-11). Duplicate occurrence of any number will be placed after indexlen(nums) - max(nums)-1
. This will happen when we swap a number to bring it to its correct position at line 11.
def find_all_duplicates(nums):i = 0while i < len(nums):j = nums[i] - 1print("\ti:",i, end="")print("\t\tnums[", i, "]: ", nums[i], sep="")print("\tj:",j, end="")print("\t\tnums[", j, "]: ", nums[j], sep="")if nums[i] != nums[j]:print("\tElement not in its place. --> Swapping " + str(nums[i]) + " and " + str(nums[j]))nums[i], nums[j] = nums[j], nums[i] # swapprint("\tnums after swap: " + str(nums))else:print("\tSame values, no need to swap.")i += 1print("\tnums after the cyclic sort",nums,sep=":")duplicateNumbers = []return duplicateNumbersdef main():nums_arr = [[1,4,4,3,2,5,6,6,7],[1,2,3,4,5,6]]for i in nums_arr:print("Find duplicates in " + str(i))result = find_all_duplicates(i)print("Duplicates are: " + str(result))print(("-"*100)+"\n")main()
- Once
nums
is sorted cyclically, the following property will be true for each of its indexi
nums[i] = i+1
, otherwise,nums[i]
is a duplicate number.
Now, scan nums
from left to right and add nums[i]
to the duplicateNumbers
whenever nums[i] != i+1
(lines 19-25).
def find_all_duplicates(nums):i = 0while i < len(nums):j = nums[i] - 1print("\ti:",i, end="")print("\t\tnums[", i, "]: ", nums[i], sep="")print("\tj:",j, end="")print("\t\tnums[", j, "]: ", nums[j], sep="")if nums[i] != nums[j]:print("\tElement not in its place. --> Swapping " + str(nums[i]) + " and " + str(nums[j]))nums[i], nums[j] = nums[j], nums[i] # swapprint("\tnums after swap: " + str(nums))else:print("\tSame values, no need to swap.")i += 1print("\tnums after the cyclic sort",nums,sep=":")duplicateNumbers = []print("\tTraversing " + str(nums) + " to find duplicate numbers")for i in range(len(nums)):print("\tComparing value ("+str(nums[i]) + ") with place (" + str(i+1) + ")")if nums[i] != i + 1:print("\tDuplicate FOUND --> Appending " + str(nums[i]) + " to duplicateNumbers")duplicateNumbers.append(nums[i])else:print("\tNo duplicate found at index: " + str(i+1))return duplicateNumbersdef main():nums_arr = [[1,4,4,3,2,5,6,6,7],[1,2,3,4,5,6]]for i in nums_arr:print("Find duplicates in " + str(i))result = find_all_duplicates(i)print("Duplicates are: " + str(result))print(("-"*100)+"\n")main()