Duplicate Zeros: Example 1
Duplicate Zeros: Example 1
Given a fixed length array arr of integers, duplicate each occurrence of zero, shifting the
remaining elements to the right.
Note that elements beyond the length of the original array are not written.
Do the above modifications to the input array in place, do not return anything from your
function.
Example 1:
Input: [1,0,2,3,0,4,5,0]
Output: null
Explanation: After calling your function, the input array is modified to:
[1,0,0,2,3,0,0,4]
Example 2:
Input: [1,2,3]
Output: null
Explanation: After calling your function, the input array is modified to:
[1,2,3]
Note:
This is a great introductory problem for understanding and working with the concept of in-place
operations. The problem statement clearly states that we are to modify the array in-place. That
does not mean we cannot use another array. We just don't have to return anything.
A better way to solve this would be without using additional space. The only reason the problem
statement allows you to make modifications in place is that it hints at avoiding any additional
memory.
The main problem with not using additional memory is that we might override elements due to
the zero duplication requirement of the problem statement. How do we get around that?
If we had enough space available, we would be able to accommodate all the elements properly.
The new length would be the original length of the array plus the number of zeros. Can we use
this information somehow to solve the problem?
The problem demands the array to be modified in-place. If in-place was not a constraint we
might have just copied the elements from a source array to a destination array.
1. s=0
2. d=0
3. for s in range(N):
4. if source[s] == 0:
5. destination[d] = 0
6. d+=1
7. destination[d]=0
8. else:
9. destination[d]=source[s]
10. d+=1
The problem statement also mentions that we do not grow the new array, rather we just trim it
to its original array length. This means we have to discard some elements from the end of the
array. These are the elements whose new indices are beyond the length of the original array.
Let's remind ourselves about the problem constraint that we are given. Since we can't use extra
space, our source and destination array is essentially the same. We just can't go about copying
the source into destination array the same way. If we do that we would lose some elements.
Since, we would be overwriting the array.
Keeping this in mind, in the approach below we start copying to the end of the array.
If we know the number of elements which would be discarded from the end of the array, we can
copy the rest. How do we find out how many elements would be discarded in the end? The
number would be equal to the number of extra zeros which would be added to the array. The
extra zero would create space for itself by pushing out an element from the end of the array.
Once we know how many elements from the original array would be part of the final array, we
can just start copying from the end. Copying from the end ensures we don't lose any element
since, the last few extraneous elements can be overwritten.
Algorithm
1. Find the number of zeros which would be duplicated. Let's call it possible_dups. We do
need to make sure we are not counting the zeros which would be trimmed off. Since, the
discarded zeros won't be part of the final array. The count of possible_dups would give
us the number of elements to be trimmed off the original array. Hence at any
point, length_ - possible_dups is the number of elements which would be included
in the final array.
Note: In the diagram above we just show source and destination array for understanding
purpose. We will be doing these operations only on one array.
1. Handle the edge case for a zero present on the boundary of the leftover elements.
Let's talk about the edge case of this problem. We need to be extra careful when we are
duplicating the zeros in the leftover array. This care should be taken for the zero which is
lying on the boundary. Since, this zero might be counted as with possible duplicates, or
may be just got included in the left over when there was no space left to accommodate
its duplicate. If it is part of the possible_dups we would want to duplicate it otherwise
we don't.
2. Iterate the array from the end and copy a non-zero element once and zero element twice.
When we say we discard the extraneous elements, it simply means we start from the left
of the extraneous elements and start overwriting them with new values, eventually right
shifting the left over elements and creating space for all the duplicated elements in the
array.
1. class Solution:
2. def duplicateZeros(self, arr: List[int]) -> None:
3. """
4. Do not return anything, modify arr in-place instead.
5. """
6.
7. possible_dups = 0
8. length_ = len(arr) - 1
9.
10. # Find the number of zeros to be duplicated
11. for left in range(length_ + 1):
12.
13. # Stop when left points beyond the last element in the
original list
14. # which would be part of the modified list
15. if left > length_ - possible_dups:
16. break
17.
18. # Count the zeros
19. if arr[left] == 0:
20. # Edge case: This zero can't be duplicated. We have
no more space,
21. # as left is pointing to the last element which
could be included
22. if left == length_ - possible_dups:
23. arr[length_] = 0 # For this zero we just copy
it without duplication.
24. length_ -= 1
25. break
26. possible_dups += 1
27.
28. # Start backwards from the last element which would be part
of new list.
29. last = length_ - possible_dups
30.
31. # Copy zero twice, and non zero once.
32. for i in range(last, -1, -1):
33. if arr[i] == 0:
34. arr[i + possible_dups] = 0
35. possible_dups -= 1
36. arr[i + possible_dups] = 0
37. else:
38. arr[i + possible_dups] = arr[i]
39.
Complexity Analysis
Array Deletions
Now that we know how insertion works, it's time to look at its complement—
deletion!
Deletion in an Array works in a very similar manner to insertion, and has the same
three different cases:
Deletion at the end of an Array is similar to people standing in a line, also known as
a queue. The person who most recently joined the queue (at the end) can leave at any
time without disturbing the rest of the queue. Deleting from the end of an Array is
the least time consuming of the three cases. Recall that insertion at the end of an
Array was also the least time-consuming case for insertion.
So, how does this work in code? Before we look at this, let's quickly remind ourselves
what the length of an Array means. Here is some code that creates an Array with
room for 10 elements, and then adds elements into the first 6 indexes of it.
int length = 0;
intArray[length] = i;
length++;
Notice the length variable. Essentially, this variable keeps track of the next index that
is free for inserting a new element. This is always the same value as the overall length
of the Array. Note that when we declare an Array of a certain size, we simply fix the
maximum number of elements it could contain. Initially, the Array doesn't contain
anything. Thus, when we add new elements, we also increment the length variable
accordingly.
Anyway, here's the code for deleting the last element of an Array.
// of the array by 1.
length--;
Well, if we run it here, we'll get the following result, regardless of whether we run it
before or after removing the last element.
Index 0 contains 0.
Index 1 contains 1.
Index 2 contains 2.
Index 3 contains 3.
Index 4 contains 4.
Index 5 contains 5.
Index 6 contains 0.
Index 7 contains 0.
Index 8 contains 0.
Index 9 contains 0.
What's gone wrong? Well, remember how there's two different definitions of length?
When we use intArray.length, we're looking every valid index of the Array. When in
fact, we only want to look at the ones that we've put values into. The fix is easy, we
just iterate up to our own length variable instead.
Run this, and you'll get the following before the deletion:
Index 0 contains 0.
Index 1 contains 1.
Index 2 contains 2.
Index 3 contains 3.
Index 4 contains 4.
Index 5 contains 5.
Index 0 contains 0.
Index 1 contains 1.
Index 2 contains 2.
Index 3 contains 3.
Index 4 contains 4.
Yup, that's it! Even though we call it a deletion, its not like we actually freed up the
space for a new element, right? This is because we don't actually need to free up any
space. Simply overwriting the value at a certain index deletes the element at that
index. Seeing as the length variable in our examples tells us the next index where we
can insert a new element, reducing it by one ensures the next new element is written
over the deleted one. This also indicates that the Array now contains one less
element, which is exactly what we want programmatically.
Deleting From the Start of an Array
Next comes the costliest of all deletion operations for an Array—deleting the first
element. If we want to delete the first element of the Array, that will create a vacant
spot at the 0th index. To fill that spot, we will shift the element at index 1 one step to
the left. Going by the ripple effect, every element all the way to the last one will be
shifted one place to the left. This shift of elements takes O(N)O(N) time,
where NN is the number of elements in the Array.
// to the left.
int_array[i - 1] = int_array[i];
// get added.
length--;
Starting from index 0, we'll move every element one position to its left, effectively
"deleting" the element at index 0. We also need to reduce length by 1 so that the
next new element is inserted in the correct position.
Index 0 contains 1.
Index 1 contains 2.
Index 2 contains 3.
Index 3 contains 4.
For deletion at any given index, the empty space created by the deleted item will
need to be filled. Each of the elements to the right of the index we're deleting at will
get shifted to the left by one. Deleting the first element of an Array is a special case
of deletion at a given index, where the index is 0. This shift of elements
takes O(K)O(K) time where KK is the number of elements to the right of the given
index. Since potentially K = NK=N, we say that the time complexity of this
operation is also O(N)O(N).
Here is the code to delete the element at index 1. To do this, we'll need to move over
the elements after it in the Array.
int_array[i - 1] = int_array[i];
length--;
Notice that this works exactly like deleting the first element, except that we don't
touch the elements that are at lower indexes than the element we're deleting.
Index 0 contains 1.
Index 1 contains 3.
Index 2 contains 4.
Did that all make sense? To help you cement what you've learned, here's a couple of
programming problems for you to try. You should try to solve them without making
a new Array. Do this by using the deletion techniques we've investigated above.
Remove Element
Given an array nums and a value val, remove all instances of that value in-place and return the
new length.
Do not allocate extra space for another array, you must do this by modifying the input array in-
place with O(1) extra memory.
The order of elements can be changed. It doesn't matter what you leave beyond the new length.
Clarification:
Confused why the returned value is an integer but your answer is an array?
Note that the input array is passed in by reference, which means a modification to the input
array will be known to the caller as well.
// using the length returned by your function, it prints the first len
elements.
print(nums[i]);
Example 1:
Explanation: Your function should return length = 2, with the first two
elements of nums being 2.
It doesn't matter what you leave beyond the returned length. For example if
you return 2 with nums = [2,2,3,3] or nums = [2,3,0,0], your answer will be
accepted.
Example 2:
Input: nums = [0,1,2,2,3,0,4,2], val = 2
Explanation: Your function should return length = 5, with the first five
elements of nums containing 0, 1, 3, 0, and 4. Note that the order of those
five elements can be arbitrary. It doesn't matter what values are set
beyond the returned length.
Constraints:
This is a pretty easy problem, but one may get confused by the term "in-place" and think it is
impossible to remove an element from the array without making a copy of the array.
Hints
1. Try two pointers.
2. Did you use the fact that the order of elements can be changed?
3. What happens when the elements to remove are rare?
Solution
Since this question is asking us to remove all elements of the given value in-place, we have to
handle it with O(1)O(1) extra space. How to solve it? We can keep two pointers ii and jj,
where ii is the slow-runner while jj is the fast-runner.
Algorithm
This solution is very similar to the solution to Remove Duplicates from Sorted Array.
Complexity analysis
Now consider cases where the array contains few elements to remove. For example, nums =
[1,2,3,5,4], val = 4nums=[1,2,3,5,4],val=4. The previous algorithm will do unnecessary copy
operation of the first four elements. Another example is nums = [4,1,2,3,5], val =
4nums=[4,1,2,3,5],val=4. It seems unnecessary to move elements [1,2,3,5][1,2,3,5] one step
left as the problem description mentions that the order of elements could be changed.
Algorithm
When we encounter nums[i] = valnums[i]=val, we can swap the current element out with the
last element and dispose the last one. This essentially reduces the array's size by 1.
Note that the last element that was swapped in could be the value you want to remove itself. But
don't worry, in the next iteration we will still check this element.
Complexity analysis
class Solution:
#i=0
# for j in range(len(nums)):
# if nums[j]!=val:
# nums[i]=nums[j]
# i+=1
# return i
n=len(nums)
i=0
while(i<n):
if nums[i]==val:
nums[i]=nums[n-1]
n-=1
else:
i+=1
return n
Do not allocate extra space for another array, you must do this by modifying the input array in-
place with O(1) extra memory.
Clarification:
Confused why the returned value is an integer but your answer is an array?
Note that the input array is passed in by reference, which means a modification to the input
array will be known to the caller as well.
// using the length returned by your function, it prints the first len
elements.
print(nums[i]);
Example 1:
Example 2:
Constraints:
Solution
Since the array is already sorted, we can keep two pointers ii and jj, where ii is the slow-runner
while jj is the fast-runner. As long as nums[i] = nums[j]nums[i]=nums[j], we increment jj to
skip the duplicate.
class Solution:
i=0
for j in range(1,len(nums)):
if nums[j]!=nums[i]:
i+=1
nums[i]=nums[j]
return i+1
Check If N and Its Double Exist
Given an array arr of integers, check if there exists two integers N and M such that N is the double
of M ( i.e. N = 2 * M).
i != j
0 <= i, j < arr.length
arr[i] == 2 * arr[j]
Example 1:
Output: true
Example 2:
Output: true
Example 3:
Output: false
Constraints:
2 <= arr.length <= 500
-10^3 <= arr[i] <= 10^3
Loop from i = 0 to arr.length, maintaining in a hashTable the array elements from [0, i -
1].
On each step of the loop check if we have seen the element 2 * arr[i] so far or arr[i] / 2
was seen if arr[i] % 2 == 0.
class Solution:
seen=set()
return True
seen.add(num)
return False
arr.length >= 3
There exists some i with 0 < i < arr.length - 1 such that:
o arr[0] < arr[1] < ... < arr[i - 1] < A[i]
o arr[i] > arr[i + 1] > ... > arr[arr.length - 1]
Example 1:
Output: false
Example 2:
Output: false
Example 3:
Output: true
Constraints:
Hide Hint #1
It's very easy to keep track of a monotonically increasing or decreasing ordering of elements. You
just need to be able to determine the start of the valley in the mountain and from that point
onwards, it should be a valley i.e. no mini-hills after that. Use this information in regards to the
values in the array and you will be able to come up with a straightforward solution.
Solution
If we walk along the mountain from left to right, we have to move strictly up, then strictly down.
Algorithm
Let's walk up from left to right until we can't: that has to be the peak. We should ensure the peak
is not the first or last element. Then, we walk down. If we reach the end, the array is valid,
otherwise its not.
class Solution(object):
N = len(A)
i=0
# walk up
i += 1
if i == 0 or i == N-1:
return False
# walk down
i += 1
return i == N-1
Complexity Analysis
Example 1:
Output: [18,6,6,6,1,-1]
Constraints:
Hide Hint #1
Loop through the array starting from the end.
Hide Hint #2
Keep the maximum value seen so far.
class Solution:
for i in range(len(arr)):
if i != len(arr)-1:
arr[i]=max(arr[i+1:])
else:
arr[i]= -1
return arr
Let's look at one more example. This time, the result Array is smaller than the input
Array! How's this going to work? Let's find out! Here's the problem description:
Given a sorted array, remove the duplicates such that each element appears only
once.
Output: [1, 2]
Output: [0, 1, 2, 3, 4]
You've hopefully already done this question, back when we were looking at deleting
items from an Array. In that case, your algorithm might have looked something like
this.
class Solution {
nums[j - 1] = nums[j];
length--;
return length;
This is actually an in-place algorithm, because it doesn't require any extra space—its
space complexity is �(1)O(1)O(1). However, the time complexity's not so flash,
at �(�2)O(N^2)O(N2). This is because of the nested loop.
One potential problem is that we actually don't know how long the result Array
needs to be. Remember how that must be decided when the Array is created? The
best solution for this problem is to do an initial pass, counting the number of unique
elements. Then, we can create the result Array and do a second pass to add the
elements into it. Here's the code for this approach.
return nums;
int uniqueNumbers = 0;
uniqueNumbers++;
int positionInResult = 0;
// Same condition as in the previous loop. Except this time, we can write
// each unique number into the result Array instead of just counting them.
result[positionInResult] = nums[i];
positionInResult++;
return result;
Did you notice the fatal flaw with this approach though? It's the wrong return type!
We could copy the result array back into the input array... and then return the
length... but this is not what the question wants us to do. We want to instead do the
deletions with a space complexity of �(1)O(1)O(1) and a time complexity
of �(�)O(N)O(N).
Have a go at this for yourself, and then we'll talk about the solution. Your algorithm
must be in-place, and take no more than �(�)O(N)O(N) time. Good luck!
Do not allocate extra space for another array, you must do this by modifying the input array in-
place with O(1) extra memory.
Clarification:
Confused why the returned value is an integer but your answer is an array?
Note that the input array is passed in by reference, which means a modification to the input
array will be known to the caller as well.
// using the length returned by your function, it prints the first len
elements.
print(nums[i]);
Example 1:
Example 2:
Constraints:
Hide Hint #1
In this problem, the key point to focus on is the input array being sorted. As far as duplicate
elements are concerned, what is their positioning in the array when the given array is sorted?
Look at the image above for the answer. If we know the position of one of the elements, do we
also know the positioning of all the duplicate elements?
Hide Hint #2
We need to modify the array in-place and the size of the final array would potentially be smaller
than the size of the input array. So, we ought to use a two-pointer approach here. One, that
would keep track of the current element in the original array and another one for just the unique
elements.
Hide Hint #3
Essentially, once an element is encountered, you simply need to bypass its duplicates and move
on to the next unique element.
1. Read all the elements like we did before, to identify the duplicates. We call this
our readPointer.
2. Keep track of the next position in the front to write the next unique element we've
found. We call this our writePointer.
if (nums == null) {
return 0;
// The first element shouldn't be touched; it's already in its correct place.
int writePointer = 1;
// element...
nums[writePointer] = nums[readPointer];
writePointer++;
}
// This turns out to be the correct length value.
return writePointer;
You're quite possibly surprised that this even works. How are we not overwriting any
elements that we haven't yet looked at?! The key thing to notice is that the condition
is such that it is impossible for writePointer to ever get ahead of the readPointer. This
means that we would never overwrite a value that we haven't yet read
This was just a very brief introduction to the very versatile and widely used two-
pointer technique. It is one of the main techniques used for in-place Array
algorithms. We'll be looking at it further in the next Array explore card!
It's important to know when to use in-place Array operations—they might not always
be the way to go.
For example, if we'll need the original array values again later, then we shouldn't be
overwriting them. In these cases, it's best to create a copy to work with, or to simply
not use in-place techniques. It's important to be very careful when working with
existing code that somebody else has written. If other code is depending on the
original Array to work, then you might completely break the program if you modify
that Array!
In-place operations are valuable when appropriate because they reduce the space
complexity of an algorithm. Instead of requiring �(�)O(N)O(N) space, we can
reduce it down to �(1)O(1)O(1).
Given an array nums, write a function to move all 0's to the end of it while maintaining the relative
order of the non-zero elements.
Example:
Input: [0,1,0,3,12]
Output: [1,3,12,0,0]
Note:
Hide Hint #1
In-place means we should not be allocating any space for extra array. But we are allowed to
modify the existing array. However, as a first step, try coming up with a solution that makes use
of additional space. For this problem as well, first apply the idea discussed using an additional
array and the in-place solution will pop up eventually.
Hide Hint #2
A two-pointer approach could be helpful here. The idea would be to have one pointer for
iterating the array and another pointer that just works on the non-zero elements of the array.
Solution
This question comes under a broad category of "Array Transformation". This category is the meat
of tech interviews. Mostly because arrays are such a simple and easy to use data structure.
Traversal or representation doesn't require any boilerplate code and most of your code will look
like the Pseudocode itself.
It's good to realize here that both the requirements are mutually exclusive, i.e., you can solve the
individual sub-problems and then combine them for the final solution.
int n = nums.size();
int numZeroes = 0;
for (int i = 0; i < n; i++) {
vector<int> ans;
if (nums[i] != 0) {
ans.push_back(nums[i]);
while (numZeroes--) {
ans.push_back(0);
nums[i] = ans[i];
Complexity Analysis
Space Complexity : �(�)O(n)O(n). Since we are creating the "ans" array to store results.
This is a 2 pointer approach. The fast pointer which is denoted by variable "cur" does the job of
processing new elements. If the newly found element is not a 0, we record it just after the last
found non-0 element. The position of last found non-0 element is denoted by the slow pointer
"lastNonZeroFoundAt" variable. As we keep finding new non-0 elements, we just overwrite them
at the "lastNonZeroFoundAt + 1" 'th index. This overwrite will not result in any loss of data
because we already processed what was there(if it were non-0,it already is now written at it's
corresponding index,or if it were 0 it will be handled later in time).
After the "cur" index reaches the end of array, we now know that all the non-0 elements have
been moved to beginning of array in their original order. Now comes the time to fulfil other
requirement, "Move all 0's to the end". We now simply need to fill all the indexes after the
"lastNonZeroFoundAt" index with 0.
C++
int lastNonZeroFoundAt = 0;
if (nums[i] != 0) {
nums[lastNonZeroFoundAt++] = nums[i];
nums[i] = 0;
Complexity Analysis
Time Complexity: �(�)O(n)O(n). However, the total number of operations are still sub-optimal.
The total operations (array writes) that code does is �nn (Total number of elements).
The optimal approach is again a subtle extension of above solution. A simple realization is if the
current element is non-0, its' correct position can at best be it's current position or a position
earlier. If it's the latter one, the current position will be eventually occupied by a non-0 ,or a 0,
which lies at a index greater than 'cur' index. We fill the current position by 0 right away,so that
unlike the previous solution, we don't need to come back here in next iteration.
With this invariant in-place, it's easy to see that the algorithm will work.
C++
if (nums[cur] != 0) {
swap(nums[lastNonZeroFoundAt++], nums[cur]);
}
Complexity Analysis
Time Complexity: �(�)O(n)O(n). However, the total number of operations are optimal. The
total operations (array writes) that code does is Number of non-0 elements.This gives us a much
better best-case (when most of the elements are 0) complexity than last solution. However, the
worst-case (when all elements are non-0) complexity for both the algorithms is same.
You may return any answer array that satisfies this condition.
Example 1:
Input: [3,1,2,4]
Output: [2,4,3,1]
Note: