Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Sort

Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 9

Problem Statement: Given an array of n integers, sort the array using the Quicksort method.

Example 1:
Input: N = 5 , Arr[] = {4,1,7,9,3}
Output: 1 3 4 7 9
Example 2:
Input: N = 8 , Arr[] = {4,6,2,5,7,9,1,3}
Output: 1 2 3 4 5 6 7 9

Intuition:

Quick Sort is a divide-and-conquer algorithm like the Merge Sort. But unlike Merge sort, this
algorithm does not use any extra array for sorting(though it uses an auxiliary stack space). So, from
that perspective, Quick sort is slightly better than Merge sort.

This algorithm is basically a repetition of two simple steps that are the following:

 Pick a pivot and place it in its correct place in the sorted array.
 Shift smaller elements(i.e. Smaller than the pivot) on the left of the pivot and larger ones to
the right.

Now, let’s discuss the steps in detail considering the array {4,6,2,5,7,9,1,3}:

Step 1: The first thing is to choose the pivot. A pivot is basically a chosen element of the given
array. The element or the pivot can be chosen by our choice. So, in an array a pivot can be any of the
following:

 The first element of the array


 The last element of the array
 Median of array
 Any Random element of the array

After choosing the pivot(i.e. the element), we should place it in its correct position(i.e. The place it
should be after the array gets sorted) in the array. For example, if the given array is {4,6,2,5,7,9,1,3},
the correct position of 4 will be the 4th position.

Note: Here in this tutorial, we have chosen the first element as our pivot. You can choose any element
as per your choice.
Step 2: In step 2, we will shift the smaller elements(i.e. Smaller than the pivot) to the left of the pivot
and the larger ones to the right of the pivot. In the example, if the chosen pivot is 4, after performing
step 2 the array will look like: {3, 2, 1, 4, 6, 5, 7, 9}.

From the explanation, we can see that after completing the steps, pivot 4 is in its correct position with
the left and right subarray unsorted. Now we will apply these two steps on the left subarray and
the right subarray recursively. And we will continue this process until the size of the unsorted part
becomes 1(as an array with a single element is always sorted).

So, from the above intuition, we can get a clear idea that we are going to use recursion in this
algorithm.

To summarize, the main intention of this process is to place the pivot, after each recursion call, at its
final position, where the pivot should be in the final sorted array.

Approach:

In the intuition, we have seen that the given array should be broken down into subarrays. But while
implementing, we are not going to break down and create any new arrays. Instead, we will specify the
range of the subarrays using two indices or pointers(i.e. low pointer and high pointer) each time while
breaking down the array.

The algorithm steps are the following for the quickSort() function:

1. Initially, the low points to the first index and the high points to the last index(as the range is n
i.e. the size of the array).
2. After that, we will get the index(where the pivot should be placed after sorting) while shifting
the smaller elements on the left and the larger ones on the right using a partition() function
discussed later.
Now, this index can be called the partition index as it creates a partition between the left and
the right unsorted subarrays.
3. After placing the pivot in the partition index(within the partition() function specified), we
need to call the function quickSort() for the left and the right subarray recursively. So, the
range of the left subarray will be [low to (partition index – 1)] and the range of the right
subarray will be [(partition index + 1) to high].
4. This is how the recursion will continue until the range becomes 1.

Pseudocode:
Now, let’s understand how to implement the partition() function to get the partition index.

1. Inside the function, we will first select the pivot(i.e. arr[low] in our case).
2. Now, we will again take two-pointers i and j. The i pointer points to low and the j points to
high.
3. Now, the pointer i will move forward and find the first element that is greater than the pivot.
Similarly, the pointer j will move backward and find the first element that is smaller than the
pivot.
Here, we need to add some checks like i <= high-1 and j >= low+1. Because it might happen
that i is standing at high and trying to proceed or j is standing at low and trying to exceed.
4. Once we find such elements i.e. arr[i] > pivot and arr[j] < pivot, and i < j, we will swap arr[i]
and arr[j].
5. We will continue step 3 and step 4, until j becomes smaller than i.
6. Finally, we will swap the pivot element(i.e. arr[low]) with arr[j] and will return the index j i.e.
the partition index.

Pseudocode:

Note: In the function, we have kept the elements equal to the pivot on the left side. If you choose to
place them on the right, check 1 will be arr[i] < pivot and check 2 will be arr[j] >= pivot.
Output:
Before Using quick Sort:
46257913
After Using quick sort:
12345679

Time Complexity: O(N*logN), where N = size of the array.

Reason: At each step, we divide the whole array, for that logN and n steps are taken for the partition()
function, so overall time complexity will be N*logN.

The following recurrence relation can be written for Quick sort : F(n) = F(k) + F(n-1-k)

Here k is the number of elements smaller or equal to the pivot and n-1-k denotes elements greater than
the pivot.

Space Complexity: O(1) + O(N) auxiliary stack space.


Worst Case Complexity [Big-O]: O(n2)

It occurs when the pivot element picked is either the greatest or the smallest element.

This condition leads to the case in which the pivot element lies in an extreme end of the sorted array.
One sub-array is always empty and another sub-array contains n - 1 elements. Thus, quicksort is

called only on this sub-array. However, the quicksort algorithm has better performance for scattered

pivots.

This case occurs when the pivot is the greatest or smallest element of the array. If the partition is done
and the last element is the pivot, then the worst case would be either in the increasing order of the
array or in the decreasing order of the array.

Recurrence: F(n) = F(0)+F(n-1) or F(n) = F(n-1) + F(0)


Best Case Complexity [Big-omega]: O(n*log n)

It occurs when the pivot element is always the middle element or near to the middle element.

This case occurs when the pivot is the middle element or near to middle element of the array.
Recurrence :
F(n) = 2F(n/2)

Time Complexity for the best and average case: O(N*logN)


Average Case Complexity [Big-theta]: O(n*log n)

It occurs when the above conditions do not occur.


Advantages of Quick Sort:
 It is a divide-and-conquer algorithm that makes it easier to solve problems.
 It is efficient on large data sets.
 It has a low overhead, as it only requires a small amount of memory to function.
Disadvantages of Quick Sort:
 It has a worst-case time complexity of O(N2), which occurs when the pivot is chosen
poorly.
 It is not a good choice for small data sets.
 It is not a stable sort, meaning that if two elements have the same key, their relative
order will not be preserved in the sorted output in case of quick sort, because here we
are swapping elements according to the pivot’s position (without considering their
original positions).
Below given the uses and real-time application of Quicksort:
 Commercial Computing is used in various government and private organizations for
the purpose of sorting various data like sorting files by name/date/price, sorting of
students by their roll no., sorting of account profile by given id, etc.
 The sorting algorithm is used for information searching and as Quicksort is the fastest
algorithm so it is widely used as a better way of searching.
 It is used everywhere where a stable sort is not needed.
 Quicksort is a cache-friendly algorithm as it has a good locality of reference when used
for arrays.
 It is tail -recursive and hence all the call optimization can be done.
 It is an in-place sort that does not require any extra storage memory.
 It is used in operational research and event-driven simulation.
 Numerical computations and in scientific research, for accuracy in calculations most of
the efficiently developed algorithm uses priority queue and quick sort is used for
sorting.
 Variants of Quicksort are used to separate the Kth smallest or largest elements.
 It is used to implement primitive type methods.
 If data is sorted then the search for information became easy and efficient.
Problem: Given an array of size n, sort the array using Merge Sort.
Example 1:

Input: N=5, arr[]={4,2,1,6,7}

Output: 1,2,4,6,7,

Example 2:

Input: N=7,arr[]={3,2,8,5,1,4,23}

Output: 1,2,3,4,5,8,23

Intuition:

1. Merge Sort is a divide and conquers algorithm, it divides the given array into equal parts
and then merges the 2 sorted parts.
2. There are 2 main functions :
merge(): This function is used to merge the 2 halves of the array. It assumes that both parts of
the array are sorted and merges both of them.
mergeSort(): This function divides the array into 2 parts. low to mid and mid+1 to high
where,

low = leftmost index of the array

high = rightmost index of the array

mid = Middle index of the array

3. We recursively split the array, and go from top-down until all sub-arrays size becomes 1.

Approach:

 We will be creating 2 functions mergeSort() and merge().


 mergeSort(arr[], low, high):
1. In order to implement merge sort we need to first divide the given array into two
halves. Now, if we carefully observe, we need not divide the array and create a
separate array, but we will divide the array’s range into halves every time. For
example, the given range of the array is 0 to 4(0-based indexing). Now on the
first go, we will divide the range into half like (0+4)/2 = 2. So, the range of the
left half will be 0 to 2 and for the right half, the range will be 3 to 4. Similarly, if
the given range is low to high, the range for the two halves will be low to mid and
mid+1 to high, where mid = (low+high)/2. This process will continue until the
range size becomes.
2. So, in mergeSort(), we will divide the array around the middle index(rather than
creating a separate array) by making the recursive call :
1. mergeSort(arr,low,mid) [Left half of the array]
2. mergeSort(arr,mid+1,high) [Right half of the array]
where low = leftmost index of the array, high = rightmost index of the array, and
mid = middle index of the array.
3. Now, in order to complete the recursive function, we need to write the base case
as well. We know from step 2.1, that our recursion ends when the array has only
1 element left. So, the leftmost index, low, and the rightmost index high become
the same as they are pointing to a single element.
Base Case: if(low >= high) return;
 Pseudocode:

 merge(arr[], low, mid, high):


1. In the merge function, we will use a temp array to store the elements of the two
sorted arrays after merging. Here, the range of the left array is low to mid and the
range for the right half is mid+1 to high.
2. Now we will take two pointers left and right, where left starts from low and right
starts from mid+1.
3. Using a while loop( while(left <= mid && right <= high)), we will select two
elements, one from each half, and will consider the smallest one among the two.
Then, we will insert the smallest element in the temp array.
4. After that, the left-out elements in both halves will be copied as it is into the temp
array.
5. Now, we will just transfer the elements of the temp array to the range low to high
in the original array. The pseudo code will look like the following:
The red number shows the order of the steps in recursion calls.

You might also like