Algrithm: Sort

15 minute read

O(n2) algorithms

1.Bubble Sort

The algorithm works by comparing each item in the list with the item next to it, and swapping them if required. In other words, the largest element has bubbled to the top of the array. The algorithm repeats this process until it makes a pass all the way through the list without swapping any items.

void bubbleSort(int ar[])
{
   for (int i = (ar.length - 1); i >= 0; i--)
   {
      for (int j = 1; j ≤ i; j++)
      {
         if (ar[j-1] > ar[j])
         {
              int temp = ar[j-1];
              ar[j-1] = ar[j];
              ar[j] = temp;
   } } } }

Example. Here is one step of the algorithm. The largest element - 7 - is bubbled to the top:

7, 5, 2, 4, 3, 9
5, 7, 2, 4, 3, 9
5, 2, 7, 4, 3, 9
5, 2, 4, 7, 3, 9
5, 2, 4, 3, 7, 9
5, 2, 4, 3, 7, 9

The worst-case runtime complexity is O(n2).

2.Selection Sort

Step 1  − Set MIN to location 0
Step 2 − Search the minimum element in the list
Step 3 − Swap with value at location MIN
Step 4 − Increment MIN to point to next element
Step 5 − Repeat until list is sorted
void selectionSort(int[] ar){
   for (int i = 0; i ‹ ar.length-1; i++)
   {
      int min = i;
      for (int j = i+1; j ‹ ar.length; j++)
            if (ar[j] ‹ ar[min]) min = j;
      int temp = ar[i];
      ar[i] = ar[min];
      ar[min] = temp;
} }
Example:
Red: Minimum value found in left array
Yellow: Min location ( the first position in unsorted-array)
Swap red and yellow

29, 64, 73, 34, 20
20, 64, 73, 34, 29
20, 29, 73, 34, 64
20, 29, 34, 73, 64
20, 29, 34, 64, 73

The worst-case runtime complexity is O(n2).

3.Insertion Sort

Step 1 − Assume first element is already sorted;
Step 2 − Pick from second element to end; 
Step 3 − Compare with all elements in the sorted sub-array, the sub-array befroe Target. compare from [j-1] to 0;
Step 4 − Shift all the elements (move to [j], inorder to leave old [j-1] to Target) in the sorted sub-array that is greater than the Target.
Step 5 − Insert the Target to j ( Target > ar[j-1] and set ar[j] = Target or compare to 0, set ar[0] = target);
Step 6 − Repeat until array is sorted
void insertionSort(int[] ar)
{
   for (int i=1; i ‹ ar.length; i++)
   {
      int target = ar[i]; int j = i;
      while (j > 0 && ar[j-1] > target)
      {
           ar[j] = ar[j-1];
           j--;
      }
      ar[j] = target;
} }
Example:
Yellow: sorted sub-array
Red: Target

29, 20, 73, 34, 64
20, 29, 73, 34, 64
20, 29, 73, 34, 64
20, 29, 34, 73, 64
20, 29, 34, 64, 73

The worst-case runtimecomplexity is O(n2). The best-case runtime complexity: O(n)


O(n log n) algorithms

1.Merge Sort

Merge-sort is based on the divide-and-conquer paradigm. It involves the following three steps:

  • Divide the array into two (or more) subarrays
  • Sort each subarray (Conquer)
  • Merge them into one (in a smart way!)
Example. Consider the following array of numbers
27  10  12  25  34  16  15  31
divide it into two parts
27  10  12  25            34  16  15  31
divide each part into two parts
27  10            12  25            34  16            15  31
divide each part into two parts
27       10       12       25       34       16       15       31

How do we merge two sorted subarrays? We define three references at the front of each array. 
We keep picking the smallest element and move it to a temporary array, incrementing the corresponding indices.

merge (cleverly-!) parts
10  27            12  25            16  34            15  31
merge parts
10  12  25  27                 15  16  31  34
10
10  12  25  27                 15  16  31  34
10  12
10  12  25  27                 15  16  31  34
10  12  15
merge parts into one
10  12  15  16  25  27  31  34
import java.util.*;

public class MergerSort
{
	public static void main(String[] args)
	{
		Integer[] a = {2, 6, 3, 5, 1};
		mergeSort(a);
		System.out.println(Arrays.toString(a));
	}

	public static void mergeSort(Comparable [ ] a)
	{
		Comparable[] tmp = new Comparable[a.length];
		mergeSort(a, tmp,  0,  a.length - 1);
	}


	private static void mergeSort(Comparable [ ] a, Comparable [ ] tmp, int left, int right)
	{
        //don't have base case
        //don't need to return
		if( left < right )
		{
			int center = (left + right) / 2;
			mergeSort(a, tmp, left, center);
			mergeSort(a, tmp, center + 1, right);
			merge(a, tmp, left, center + 1, right);
		}
	}


    private static void merge(Comparable[ ] a, Comparable[ ] tmp, int left, int right, int rightEnd )
    {
        int leftEnd = right - 1;
        int k = left;
        int num = rightEnd - left + 1;

        while(left <= leftEnd && right <= rightEnd)
            if(a[left].compareTo(a[right]) <= 0)
            //k represent these two sub-array position in the old array
                tmp[k++] = a[left++];
            else
                tmp[k++] = a[right++];

        while(left <= leftEnd)    // Copy rest of first half
            tmp[k++] = a[left++];

        while(right <= rightEnd)  // Copy rest of right half
            tmp[k++] = a[right++];

        // Copy tmp back
        //tmp represent new array to store new order sub-array
        for(int i = 0; i < num; i++, rightEnd--)
            a[rightEnd] = tmp[rightEnd];
    }
 }

2.Quick Sort

Quick sort is based on the divide-and-conquer approach based on the idea of choosing one element as a pivot element and partitioning the array around it such that: Left side of pivot contains all the elements that are less than the pivot element Right side contains all elements greater than the pivot

Implementation : Select the first element of array as the pivot element First, we will see how the partition of the array takes place around the pivot.

Quick Sort

In the implementation below, the following components have been used: Here, A[] = array whose elements are to be sorted start: Leftmost position of the array end: Rightmost position of the array i: Boundary between the elements that are less than pivot and those greater than pivot j: Boundary between the partitioned and unpartitioned part of array piv: Pivot element

int partition ( int A[],int start ,int end) {
    int i = start + 1;
    int piv = A[start] ;            //make the first element as pivot element.
    for(int j =start + 1; j <= end ; j++ )  {
    /*rearrange the array by putting elements which are less than pivot
       on one side and which are greater that on other. */

        if ( A[ j ] < piv) {
            swap (A[ i ],A [ j ]);
            i += 1;
        }
   }
   swap ( A[ start ] ,A[ i-1 ] ) ;  //put the pivot element in its proper place.
   return i-1;                      //return the position of the pivot
}

Now, let us see the recursive function Quick_sort :

void quick_sort ( int A[ ] ,int start , int end ) {
   if( start < end ) {
        //stores the position of pivot element
         int piv_pos = partition (A,start , end ) ;     
         quick_sort (A,start , piv_pos -1);    //sorts the left side of pivot.
         quick_sort ( A,piv_pos +1 , end) ; //sorts the right side of pivot.
   }
}
//Let’s see the randomized version of the partition function :
//Some also set pivot as last one or check the mid value of first, last and mid
int rand_partition ( int A[ ] , int start , int end ) {
    //chooses position of pivot randomly by using rand() function .
     int random = start + rand( )%(end-start +1 ) ;

      swap ( A[random] , A[start]) ;        //swap pivot with 1st element.
     return partition(A,start ,end) ;       //call the above partition function
}

O(n) algorithms

1.Counting Sort

Let’s assume that, array of size needs to be sorted.

  • Initialize the auxillary array Aux[] as 0.
    Note: The size of this array should be .
  • Traverse array A and store the count of occurrence of each element in the appropriate index of the Aux array, which means, execute Aux[ A[ i ] ]++ for each i, where i ranges from [0, N-1].
  • Initialize the empty array sortedA[]
  • Traverse array Aux and copy i into sortedA for Aux[ i ] number of times where 0 <= i <= max[ A[] ]>.

Note: The array A can be sorted by using this algorithm only if the maximum value in array A is less than the maximum size of the array Aux. Usually, it is possible to allocate memory up to the order of a million ( 10 6). If the maximum value of exceeds the maximum memory- allocation size, it is recommended that you do not use this algorithm. Use either the quick sort or merge sort algorithm.

Implementation:

Assume that the maximum element that can be in the array is K.
Now take an Aux[] array of size K+1.
A[] = Array to be sorted.
sortedA[] = Sorted version of A[].
void counting_sort(int A[], int Aux[], int sortedA[], int N) {

    // First, find the maximum value in A[]
    int K = 0;
    for(int i=0; i<N; i++) {
        K = max(K, A[i]);
    }

    // Initialize the elements of Aux[] with 0
    for(int i=0 ; i<=K; i++) {
        Aux[i] = 0;
    }

    // Store the frequencies of each distinct element of A[],
    // by mapping its value as the index of Aux[] array
    for(int i=0; i<N; i++) {
        Aux[A[i]]++;
    }

    int j = 0;
    for(int i=0; i<=K; i++) {
        int tmp = Aux[i];
        // Aux stores which element occurs how many times,
        // Add i in sortedA[] according to the number of times i occured in A[]
        while(tmp--) {
            //cout << Aux[i] << endl;
            sortedA[j] = i;
            j++;
        }
    }
}
Example:
Say A={5, 2, 9, 5, 2, 3, 5}.

Aux will be of the size 9 + 1 i.e 10. 

Aux = {0, 0, 2, 1, 0, 3, 0, 0, 0, 2}.
Notice that Aux[2] = 2 which represents the number of occurrences of 2 in A[]. Similarly Aux[5] = 3 which represents the number occurrences of 5 in A[].

After applying the counting sort algorithm, sortedA[] will be {2, 2, 3, 5, 5, 5, 9}
Time Complexity:
The array A is traversed in O(N) time and the resulting sorted array is also computed in O(N) time. Aux is traversed in O(K) time. Therefore, the overall time complexity of counting sort algorithm is O(N+K).

2.Radix Sort

Prerequisite: Counting Sort

QuickSort, MergeSort, HeapSort are comparison based sorting algorithms.

CountSort is not comparison based algorithm. It has the complexity of O(N+K), where K is the maximum element of the input array.

So, if K is O(N),CountSort becomes linear sorting, which is better than comparison based sorting algorithms that have time O(nlogn)complexity. The idea is to extend the CountSort algorithm to get a better time complexity when k goes (N2). Here comes the idea of Radix Sort.

Algorithm:

For each digit i where i varies from the least significant digit to the most significant digit of a number Sort input array using countsort algorithm according to ith digit.
We used count sort because it is a stable sort.

Example: Assume the input array is:
10,21,17,34,44,11,654,123
Based on the algorithm, we will sort the input array according to the one's digit (least significant digit).
0: 10
1: 21 11
2:
3: 123
4: 34 44 654
5:
6:
7: 17
8:
9:

So, the array becomes 10,21,11,123,24,44,654,17
Now, we'll sort according to the ten's digit:
0:
1: 10 11 17
2: 21 123
3: 34
4: 44
5: 654
6:
7:
8:
9:

Now, the array becomes : 10,11,17,21,123,34,44,654
Finally , we sort according to the hundred's digit (most significant digit):
0: 010 011 017 021 034 044
1: 123
2:
3:
4:
5:
6: 654
7:
8:
9:

The array becomes : 10,11,17,21,34,44,123,654 which is sorted. This is how our algorithm works.

Implementation:

void countsort(int arr[],int n,int place)
{
        int i,freq[range]={0};         //range for integers is 10 as digits range from 0-9
        int output[n];
        for(i=0;i<n;i++)
                freq[(arr[i]/place)%range]++;
        for(i=1;i<range;i++)
                freq[i]+=freq[i-1];
        for(i=n-1;i>=0;i--)
        {
                output[freq[(arr[i]/place)%range]-1]=arr[i];
                freq[(arr[i]/place)%range]--;
        }
        for(i=0;i<n;i++)
                arr[i]=output[i];
}
void radixsort(ll arr[],int n,int maxx)            //maxx is the maximum element in the array
{
        int mul=1;
        while(maxx)
        {
                countsort(arr,n,mul);
                mul*=10;
                maxx/=10;
        }
}

Complexity Analysis:
The complexity is O((n+b)*logb(max x)) where b is the base(binary or decimal) for representing numbers and max x is the maximum element of the input array. This is clearly visible as we make (n+b) iterations logb(max x) times (number of digits in the maximum element) . If max x <= nc,then the complexity can be written as O(n*logb(n )).

Advantages :
1. Fast when the keys are short i.e. when the range of the array elements is less.
2. Used in suffix array constuction algorithms like Manber's algorithm and DC3 algorithm.

Disadvantages :
1. Since Radix Sort depends on digits or letters, Radix Sort is much less flexible than other sorts. Hence , for every different type of data it needs to be rewritten.
2. The constant for Radix sort is greater compared to other sorting algorithms.
3. It takes more space compared to Quicksort which is inplace sorting.

The Radix Sort algorithm is an important sorting algorithm that is integral to suffix -array construction algorithms. It is also useful on parallel machines.

3.Bucket Sort

Bucket sort is a comparison sort algorithm that operates on elements by dividing them into different buckets and then sorting these buckets individually. Each bucket is sorted individually using a separate sorting algorithm or by applying the bucket sort algorithm recursively. Bucket sort is mainly useful when the input is uniformly distributed over a range.

void bucketSort(float[] a,int n)
{
    for(each floating integer 'x' in n)
    {
        insert x into bucket[n*x]; 
    }
    for(each bucket)
    {
        sort(bucket);
    }
}

If one assumes that insertion in a bucket takes O(1) time, then steps 1 and 2 of the above algorithm clearly take O(N) time. But we dont know how much buckets we have, and sort() method time complexity.

JAVA Arrays

1.Arrays.sort()

Dual-Pivot Quick Sort:

sort(int[] a)

sort(int[] a, int fromIndex, int toIndex)

Same as: long[], short[], char[], byte[], float[], double[]

Tim Sort:

sort(Object[] a)

sort(Object[] a, int fromIndex, int toIndex)

sort(T[] a, Comparator<? super T> c)

sort(T[] a, int fromIndex, int toIndex, Comparator<? super T> c)

    public static void main(String[] args) 
    { 
        // Our arr contains 8 elements 
        int[] arr = {13, 7, 6, 45, 21, 9, 101, 102}; 
    
        Arrays.sort(arr); 
    
        System.out.printf("Modified arr[] : %s", 
                            Arrays.toString(arr)); 
    } 
    //Output: Modified arr[] : [6, 7, 9, 13, 21, 45, 101, 102]
    public static void main(String[] args) 
    { 
        // Our arr contains 8 elements 
        int[] arr = {13, 7, 6, 45, 21, 9, 2, 100}; 
    
        // Sort subarray from index 1 to 4, i.e., 
        // only sort subarray {7, 6, 45, 21} and 
        // keep other elements as it is. 
        Arrays.sort(arr, 1, 5); 
    
        System.out.printf("Modified arr[] : %s", 
                            Arrays.toString(arr)); 
    } 
    //Modified arr[] : [13, 6, 7, 21, 45, 9, 2, 100]
    public static void main(String[] args) 
    { 
        String arr[] = {"practice.geeksforgeeks.org", 
                        "quiz.geeksforgeeks.org", 
                        "code.geeksforgeeks.org"
                       }; 
  
        // Sorts arr[] in ascending order 
        Arrays.sort(arr); 
        System.out.printf("Modified arr[] : \n%s\n\n", 
                          Arrays.toString(arr)); 
  
        // Sorts arr[] in descending order 
        Arrays.sort(arr, Collections.reverseOrder()); 
  
        System.out.printf("Modified arr[] : \n%s\n\n", 
                          Arrays.toString(arr)); 
    } 
// Modified arr[] : 
// [code.geeksforgeeks.org, practice.geeksforgeeks.org, quiz.geeksforgeeks.org]

// Modified arr[] : 
// [quiz.geeksforgeeks.org, practice.geeksforgeeks.org, code.geeksforgeeks.org]
// Java program to demonstrate working of Comparator 
// interface 
import java.util.*; 
import java.lang.*; 
import java.io.*; 

// A class to represent a student. 
class Student 
{ 
	int rollno; 
	String name, address; 

	// Constructor 
	public Student(int rollno, String name, 
							String address) 
	{ 
		this.rollno = rollno; 
		this.name = name; 
		this.address = address; 
	} 

	// Used to print student details in main() 
	public String toString() 
	{ 
		return this.rollno + " " + this.name + 
						" " + this.address; 
	} 
} 

class Sortbyroll implements Comparator<Student> 
{ 
	// Used for sorting in ascending order of 
	// roll number 
	public int compare(Student a, Student b) 
	{ 
		return a.rollno - b.rollno; 
	} 
} 

// Driver class 
class Main 
{ 
	public static void main (String[] args) 
	{ 
		Student [] arr = {new Student(111, "bbbb", "london"), 
						new Student(131, "aaaa", "nyc"), 
						new Student(121, "cccc", "jaipur")}; 

		System.out.println("Unsorted"); 
		for (int i=0; i<arr.length; i++) 
			System.out.println(arr[i]); 

		Arrays.sort(arr, new Sortbyroll()); 

		System.out.println("\nSorted by rollno"); 
		for (int i=0; i<arr.length; i++) 
			System.out.println(arr[i]); 
	} 
} 
// Unsorted
// 111 bbbb london
// 131 aaaa nyc
// 121 cccc jaipur

// Sorted by rollno
// 111 bbbb london
// 121 cccc jaipur
// 131 aaaa nyc

2.Arrays.parallelSort()

sort(int[] a)

sort(int[] a, int fromIndex, int toIndex)

Same as: long[], short[], char[], byte[], float[], double[]

parallelSort(T[] a)

parallelSort(T[] a, Comparator<? super T> cmp)

parallelSort(T[] a, int fromIndex, int toIndex)

parallelSort(T[] a, int fromIndex, int toIndex, Comparator<? super T> cmp)

Parallel sort uses threading (each thread gets a chunk of the list and sorts it in parallel. Later these sorted chunks are merged into a result). It's faster when there are a lot of elements in the collection. The overhead for parallelization becomes tolerably small on larger arrays, but it is large for smaller ones.

parallel

JAVA ArrayList/LinkedList

Consider an ArrayList that stores country names as String objects. To sort the ArrayList, you need to simply call the Collections.sort() method passing the ArrayList object populated with country names. This method will sort the elements (country names) of the ArrayList using natural ordering (alphabetically in ascending order)

Collections.sort:

adapted from Tim Peters’s list sort for Python ( TimSort)

java.util.Collections.reverseOrder()

	    ArrayList<String> countryList = new ArrayList<>();         
	    countryList.add("France");         
	    countryList.add("USA");         
	    countryList.add("India");         
	    countryList.add("Spain");         
	    countryList.add("England");                        
	    System.out.println("Unsorted ArrayList: " + countryList);    
	    Collections.sort(countryList);    
	    System.out.println("Sorted ArrayList in Ascending Order : " + countryList);         
	    Collections.sort(countryList, Collections.reverseOrder());         
	    System.out.println("Sorted ArrayList in Descending Order: " + countryList);  

// Unsorted ArrayList: [France, USA, India, Spain, England]
// Sorted ArrayList in Ascending Order : [England, France, India, Spain, USA]
// Sorted ArrayList in Descending Order: [USA, Spain, India, France, England]

public static Comparator reverseOrder(Comparator c)

class Student 
{ 
    int rollno; 
    String name, address; 
  
    // Constructor 
    public Student(int rollno, String name, 
                               String address) 
    { 
        this.rollno = rollno; 
        this.name = name; 
        this.address = address; 
    } 
  
    // Used to print student details in main() 
    public String toString() 
    { 
        return this.rollno + " " + this.name + 
                           " " + this.address; 
    } 
} 
  
class Sortbyroll implements Comparator<Student> 
{ 
    // Used for sorting in ascending order of 
    // roll number 
    public int compare(Student a, Student b) 
    { 
        return a.rollno - b.rollno; 
    } 
} 
  
// Driver class 
class Main 
{ 
    public static void main (String[] args) 
    { 
        ArrayList<Student> ar = new ArrayList<Student>(); 
        ar.add(new Student(111, "bbbb", "london")); 
        ar.add(new Student(131, "aaaa", "nyc")); 
        ar.add(new Student(121, "cccc", "jaipur")); 
  
        System.out.println("Unsorted"); 
        for (int i=0; i<ar.size(); i++) 
            System.out.println(ar.get(i)); 
  
        // Sorting a list of students in descending order of 
        // roll numbers using a Comparator that is reverse of 
        // Sortbyroll() 
        Comparator c = Collections.reverseOrder(new Sortbyroll()); 
        Collections.sort(ar, c); 
  
        System.out.println("\nSorted by rollno"); 
        for (int i=0; i<ar.size(); i++) 
            System.out.println(ar.get(i)); 
    } 
} 
// Unsorted
// 111 bbbb london
// 131 aaaa nyc
// 121 cccc jaipur

// Sorted by rollno
// 131 aaaa nyc
// 121 cccc jaipur
// 111 bbbb london

Reference

https://www.cs.cmu.edu/~adamchik/15-121/lectures/Sorting%20Algorithms/sorting.html

https://medium.com/@george.seif94/a-tour-of-the-top-5-sorting-algorithms-with-python-code-43ea9aa02889

https://www.tutorialspoint.com/

https://www.hackerearth.com

https://www.geeksforgeeks.org

https://stackoverflow.com

https://dzone.com/articles/sorting-java-arraylist