0% found this document useful (0 votes)
45 views

Duplicate Zeros: Example 1

The document discusses deleting elements from an array in-place without using additional memory. It explains that deleting the last element is the simplest case, as it only requires reducing the length variable by 1. Deleting from the first or a specific index requires shifting elements to fill the gap. The key steps are: 1) Shift elements after the deleted index forward to fill the gap; 2) Reduce the length variable to reflect the new size.

Uploaded by

Aye
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
45 views

Duplicate Zeros: Example 1

The document discusses deleting elements from an array in-place without using additional memory. It explains that deleting the last element is the simplest case, as it only requires reducing the length variable by 1. Deleting from the first or a specific index requires shifting elements to fill the gap. The key steps are: 1) Shift elements after the deleted index forward to fill the gap; 2) Reduce the length variable to reflect the new size.

Uploaded by

Aye
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
You are on page 1/ 40

Duplicate Zeros

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:

1. 1 <= arr.length <= 10000


2. 0 <= arr[i] <= 9

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.

Notice, how we copied zero twice.

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.

Approach 1: Two pass, O(1) space


Intuition

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.

An example of the edge case is - [8,4,5,0,0,0,0,7]. In this array there is space to


accommodate the duplicates of first and second occurrences of zero. But we don't have
enough space for the duplicate of the third occurrence of zero. Hence when we are
copying we need to make sure for the third occurrence we don't copy twice. Result =
[8,4,5,0,0,0,0,0]

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

 Time Complexity: O(N)O(N), where NN is the number of elements in the array. We do


two passes through the array, one to find the number of possible_dups and the other
to copy the elements. In the worst case we might be iterating the entire array, when there
are less or no zeros in the array.
 Space Complexity: O(1)O(1). We do not use any extra space.

  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:

1. Deleting the last element of the Array.


2. Deleting the first element of the Array.
3. Deletion at any given index.
Deleting From the End of an Array

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.

// Declare an integer array of 10 elements.

int[] intArray = new int[10];

// The array currently contains 0 elements

int length = 0;

// Add elements at the first 6 indexes of the Array.

for(int i = 0; i < 6; i++) {

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.

// Deletion from the end is as simple as reducing the length

// of the array by 1.

length--;

Remember how insertion we were using this printArray function?

for (int i = 0; i < intArray.length; i++) {

System.out.println("Index " + i + " contains " + intArray[i]);

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.

for (int i = 0; i < length; i++) {

System.out.println("Index " + i + " contains " + intArray[i]);

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.

And the following after:

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.

Here is how deleting the first element looks in code.

// Starting at index 1, we shift each element one position

// to the left.

for (int i = 1; i < length; i++) {

// Shift each element one position to the left

int_array[i - 1] = int_array[i];

// Note that it's important to reduce the length of the array by 1.

// Otherwise, we'll lose consistency of the size. This length

// variable is the only thing controlling where new elements might

// 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.

And here's the output we'll get, with our updated printArray function.

Index 0 contains 1.

Index 1 contains 2.

Index 2 contains 3.

Index 3 contains 4.

Deleting From Anywhere in the Array

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.

// Say we want to delete the element at index 1


for (int i = 2; i < length; i++) {

// Shift each element one position to the left

int_array[i - 1] = int_array[i];

// Again, the length needs to be consistent with the current

// state of the array.

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.

Here is the output from the printArray function.

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.

Once you're done, we'll look at searching Arrays!

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.

Internally you can think of this:

// nums is passed in by reference. (i.e., without making a copy)

int len = removeElement(nums, val);

// any modification to nums in your function would be known by the caller.

// using the length returned by your function, it prints the first len
elements.

for (int i = 0; i < len; i++) {

    print(nums[i]);

Example 1:

Input: nums = [3,2,2,3], val = 3

Output: 2, nums = [2,2]

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

Output: 5, nums = [0,1,4,0,3]

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:

 0 <= nums.length <= 100


 0 <= nums[i] <= 50
 0 <= val <= 100

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

Approach 1: Two Pointers


Intuition

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

When nums[j]nums[j] equals to the given value, skip this element by incrementing jj. As long


as nums[j] \neq valnums[j]=val, we copy nums[j]nums[j] to nums[i]nums[i] and increment
both indexes at the same time. Repeat the process until jj reaches the end of the array and the
new length is ii.

This solution is very similar to the solution to Remove Duplicates from Sorted Array.

Complexity analysis

 Time complexity : O(n)O(n). Assume the array has a total of nn elements,


both ii and jj traverse at most 2n2n steps.

 Space complexity : O(1)O(1).

Approach 2: Two Pointers - when elements to remove are rare


Intuition

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

 Time complexity : O(n)O(n). Both ii and nn traverse at most nn steps. In this approach,


the number of assignment operations is equal to the number of elements to remove. So
it is more efficient if elements to remove are rare.

 Space complexity : O(1)O(1).

class Solution:

def removeElement(self, nums: List[int], val: int) -> int:

#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

Remove Duplicates from Sorted Array


Solution
Given a sorted array nums, remove the duplicates in-place such that each element appears
only once and returns 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.

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.

Internally you can think of this:

// nums is passed in by reference. (i.e., without making a copy)

int len = removeDuplicates(nums);

// any modification to nums in your function would be known by the caller.

// using the length returned by your function, it prints the first len
elements.

for (int i = 0; i < len; i++) {

    print(nums[i]);

Example 1:

Input: nums = [1,1,2]

Output: 2, nums = [1,2]

Explanation: Your function should return length = 2, with the first two


elements of nums being 1 and 2 respectively. It doesn't matter what you leave
beyond the returned length.

Example 2:

Input: nums = [0,0,1,1,1,2,2,3,3,4]


Output: 5, nums = [0,1,2,3,4]

Explanation: Your function should return length = 5, with the first five


elements of nums being modified to 0, 1, 2, 3, and 4 respectively. It doesn't
matter what values are set beyond the returned length.

Constraints:

 0 <= nums.length <= 3 * 104


 -104 <= nums[i] <= 104
 nums is sorted in ascending order.

Solution

Approach 1: Two Pointers


Algorithm

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.

When we encounter nums[j] \neq nums[i]nums[j]=nums[i], the duplicate run has ended so


we must copy its value to nums[i + 1]nums[i+1]. ii is then incremented and we repeat the same
process again until jj reaches the end of array.

class Solution:

def removeDuplicates(self, nums: List[int]) -> int:

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).

More formally check if there exists two indices i and j such that :

 i != j
 0 <= i, j < arr.length
 arr[i] == 2 * arr[j]

Example 1:

Input: arr = [10,2,5,3]

Output: true

Explanation: N = 10 is the double of M = 5,that is, 10 = 2 * 5.

Example 2:

Input: arr = [7,1,14,11]

Output: true

Explanation: N = 14 is the double of M = 7,that is, 14 = 2 * 7.

Example 3:

Input: arr = [3,1,7,11]

Output: false

Explanation: In this case does not exist N and M, such that N = 2 * M.

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:

def checkIfExist(self, arr: List[int]) -> bool:

seen=set()

for num in arr:

if 2*num in seen or num/2 in seen:

return True

seen.add(num)

return False

 Valid Mountain Array


Solution
Given an array of integers arr, return true  if and only if it is a valid mountain array.

Recall that arr is a mountain array if and only if:

 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:

Input: arr = [2,1]

Output: false

Example 2:

Input: arr = [3,5,5]

Output: false

Example 3:

Input: arr = [0,3,2,1]

Output: true

Constraints:

 1 <= arr.length <= 104


 0 <= arr[i] <= 104

   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

Approach 1: One Pass


Intuition

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):

def validMountainArray(self, A):

N = len(A)

i=0

# walk up

while i+1 < N and A[i] < A[i+1]:

i += 1

# peak can't be first or last

if i == 0 or i == N-1:

return False

# walk down

while i+1 < N and A[i] > A[i+1]:

i += 1

return i == N-1
Complexity Analysis

 Time Complexity: �(�)O(N)O(N), where �NN is the length of A.


 Space Complexity: �(1)O(1)O(1).

   In-Place Array Operations


Introduction

 In programming interviews, the interviewer often expects you to minimise the


time and space complexity of your implementation. In-place Array operations
help to reduce space complexity, and so are a class of techniques that pretty
much everybody encounters regularly in interviews.
 So, what are in-place array operations?
 The best way of answering this question is to look at an example.
 Given an Array of integers, return an Array where every element at an even-
indexed position is squared.
 Input: array = [9, -2, -9, 11, 56, -12, -3]
 Output: [81, -2, 81, 11, 3136, -12, 9]
 Explanation: The numbers at even indexes (0, 2, 4, 6) have
been squared,
 whereas the numbers at odd indexes (1, 3, 5) have been left
the same.
 This problem is hopefully very straightforward. Have a quick think about how
you would implement it as an algorithm though, possibly jotting down some
code on a piece of paper.
 Anyway, there are two ways we could approach it. The first is to create a new
Array, of the same size as the original. Then, we should copy the odd-indexed
elements and square the even-indexed elements, writing them into the new
Array.
 The above approach, although correct, is an inefficient way of solving the
problem. This is because it uses �(length)O(\text{length})O(length) extra
space.
 Instead, we could iterate over the original input Array itself, overwriting every
even-indexed element with its own square. This way, we won't need that extra
space. It is this technique of working directly in the input Array,
and not creating a new Array, that we call in-place. In-place Array operations
are a big deal for programming interviews, where there is a big focus on
minimising both time and space complexity of algorithms.
An important difference for in-place vs not in-place is that in-place modifies the
input Array. This means that other functions can no longer access the original
data, because it has been overwritten. We'll talk more about this in a bit.

Replace Elements with Greatest Element on Right


Side
Given an array arr, replace every element in that array with the greatest element among the
elements to its right, and replace the last element with -1.

After doing so, return the array.

Example 1:

Input: arr = [17,18,5,4,6,1]

Output: [18,6,6,6,1,-1]

Constraints:

 1 <= arr.length <= 10^4


 1 <= arr[i] <= 10^5

   Hide Hint #1  
Loop through the array starting from the end.

   Hide Hint #2  
Keep the maximum value seen so far.

class Solution:

def replaceElements(self, arr: List[int]) -> List[int]:

for i in range(len(arr)):
if i != len(arr)-1:

arr[i]=max(arr[i+1:])

else:

arr[i]= -1

return arr

A Better Repeated Deletion Algorithm -


Intro

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.

Input: array = [1, 1, 2]

Output: [1, 2]

Input: array = [0, 0, 1, 1, 1, 2, 2, 3, 3, 4]

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 {

public int removeDuplicates(int[] nums) {

// The initial length is simply the capacity.


int length = nums.length;

// Assume the last element is always unique.

// Then for each element, delete it iff it is

// the same as the one after it. Use our deletion

// algorithm for deleting from any index.

for (int i = length - 2; i >= 0; i--) {

if (nums[i] == nums[i + 1]) {

// Delete the element at index i, using our standard

// deletion algorithm we learned.

for (int j = i + 1; j < length; j++) {

nums[j - 1] = nums[j];

// Reduce the length by 1.

length--;

// Return the new 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.

We want to get the algorithm down to an �(�)O(N)O(N) time complexity.


If we don't try to do this in-place, then it's straightforward. We could simply iterate
through the Array, adding all unique elements to a new Array. Seeing as the the input
Array is sorted, we can easily identify all unique elements, as they are the first
element, and then any element that is different to the one before it.

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.

public int[] copyWithRemovedDuplicates(int[] nums) {

// Check for edge cases.

if (nums == null || nums.length == 0) {

return nums;

// Count how many unique elements are in the Array.

int uniqueNumbers = 0;

for (int i = 0; i < nums.length; i++) {

// An element should be counted as unique if it's the first

// element in the Array, or is different to the one before it.


if (i == 0 || nums[i] != nums[i - 1]) {

uniqueNumbers++;

// Create a result Array.

int[] result = new int[uniqueNumbers];

// Write the unique elements into the result Array.

int positionInResult = 0;

for (int i = 0; i < nums.length; i++) {

// 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.

if (i == 0 || nums[i] != nums[i - 1]) {

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!

 Remove Duplicates from Sorted Array


Solution
Given a sorted array nums, remove the duplicates in-place such that each element appears
only once and returns 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.

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.

Internally you can think of this:

// nums is passed in by reference. (i.e., without making a copy)

int len = removeDuplicates(nums);

// any modification to nums in your function would be known by the caller.

// using the length returned by your function, it prints the first len
elements.

for (int i = 0; i < len; i++) {

    print(nums[i]);

 
Example 1:

Input: nums = [1,1,2]

Output: 2, nums = [1,2]

Explanation: Your function should return length = 2, with the first two


elements of nums being 1 and 2 respectively. It doesn't matter what you leave
beyond the returned length.

Example 2:

Input: nums = [0,0,1,1,1,2,2,3,3,4]

Output: 5, nums = [0,1,2,3,4]

Explanation: Your function should return length = 5, with the first five


elements of nums being modified to 0, 1, 2, 3, and 4 respectively. It doesn't
matter what values are set beyond the returned length.

Constraints:

 0 <= nums.length <= 3 * 104


 -104 <= nums[i] <= 104
 nums is sorted in ascending order.

   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.

  A Better Repeated Deletion Algorithm -


Answer

Anyway, the algorithm with �(�)O(N)O(N) space is surprisingly similar to the one


without. Interestingly, it's simpler though, because it doesn't need to firstly
determine the size of the output.
Implementing this requires the use of the two-pointer technique. This is where we
iterate over the Array in two different places at the same time.

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.

Here's the algorithm in Java code.


public int removeDuplicates(int[] nums) {

// Check for edge cases.

if (nums == null) {

return 0;

// Use the two pointer technique to remove the duplicates in-place.

// The first element shouldn't be touched; it's already in its correct place.

int writePointer = 1;

// Go through each element in the Array.

for (int readPointer = 1; readPointer < nums.length; readPointer++) {

// If the current element we're reading is *different* to the previous

// element...

if (nums[readPointer] != nums[readPointer - 1]) {

// Copy it into the next position at the front, tracked by writePointer.

nums[writePointer] = nums[readPointer];

// And we need to now increment writePointer, because the next element

// should be written one space over.

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!

When to Use In-Place Array Operations

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:

1. You must do this in-place without making a copy of the array.


2. Minimize the total number of operations.

   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.

The 2 requirements of the question are:

Move all the 0's to the end of array.

1. All the non-zero elements must retain their original order.

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.

Approach #1 (Space Sub-Optimal) [Accepted]


C++

void moveZeroes(vector<int>& nums) {

int n = nums.size();

// Count the zeroes

int numZeroes = 0;
for (int i = 0; i < n; i++) {

numZeroes += (nums[i] == 0);

// Make all the non-zero elements retain their original order.

vector<int> ans;

for (int i = 0; i < n; i++) {

if (nums[i] != 0) {

ans.push_back(nums[i]);

// Move all zeroes to the end

while (numZeroes--) {

ans.push_back(0);

// Combine the result

for (int i = 0; i < n; i++) {

nums[i] = ans[i];

Complexity Analysis

Space Complexity : �(�)O(n)O(n). Since we are creating the "ans" array to store results.

Time Complexity: �(�)O(n)O(n). However, the total number of operations are sub-optimal. We


can achieve the same result in less number of operations.
If asked in an interview, the above solution would be a good start. You can explain the
interviewer(not code) the above and build your base for the next Optimal Solution.

Approach #2 (Space Optimal, Operation Sub-Optimal) [Accepted]


This approach works the same way as above, i.e. , first fulfills one requirement and then another.
The catch? It does it in a clever way. The above problem can also be stated in alternate way, "
Bring all the non 0 elements to the front of array keeping their relative order same".

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++

void moveZeroes(vector<int>& nums) {

int lastNonZeroFoundAt = 0;

// If the current element is not 0, then we need to

// append it just in front of last non 0 element we found.

for (int i = 0; i < nums.size(); i++) {

if (nums[i] != 0) {

nums[lastNonZeroFoundAt++] = nums[i];

// After we have finished processing new elements,

// all the non-zero elements are already at beginning of array.

// We just need to fill remaining array with 0's.


for (int i = lastNonZeroFoundAt; i < nums.size(); i++) {

nums[i] = 0;

Complexity Analysis

Space Complexity : �(1)O(1)O(1). Only constant space is used.

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).

Approach #3 (Optimal) [Accepted]


The total number of operations of the previous approach is sub-optimal. For example, the array
which has all (except last) leading zeroes: [0, 0, 0, ..., 0, 1].How many write operations to the array?
For the previous approach, it writes 0's �−1n-1n−1 times, which is not necessary. We could
have instead written just once. How? ..... By only fixing the non-0 element,i.e., 1.

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.

In other words, the code will maintain the following invariant:

1. All elements before the slow pointer (lastNonZeroFoundAt) are non-zeroes.


2. All elements between the current and slow pointer are zeroes.
Therefore, when we encounter a non-zero element, we need to swap elements pointed by
current and slow pointer, then advance both pointers. If it's zero element, we just advance current
pointer.

With this invariant in-place, it's easy to see that the algorithm will work.

C++

void moveZeroes(vector<int>& nums) {

for (int lastNonZeroFoundAt = 0, cur = 0; cur < nums.size(); cur++) {

if (nums[cur] != 0) {

swap(nums[lastNonZeroFoundAt++], nums[cur]);
}

Complexity Analysis

Space Complexity : �(1)O(1)O(1). Only constant space is used.

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.

Sort Array By Parity


Solution
Given an array A of non-negative integers, return an array consisting of all the even elements
of A, followed by all the odd elements of A.

You may return any answer array that satisfies this condition.

Example 1:

Input: [3,1,2,4]

Output: [2,4,3,1]

The outputs [4,2,3,1], [2,4,1,3], and [4,2,1,3] would also be accepted.

Note:

1. 1 <= A.length <= 5000


2. 0 <= A[i] <= 5000

You might also like