Sorting basis 2 (merge, quick sort)

Principle and implementation of merge sort

For merge sort, the key is to understand the idea of divide and conquer algorithm and recursive processing. If we want to sort an array, we should first divide the array into two segments from the middle, then sort the two segments respectively, and finally sort the sorted two parts, so that the whole array is an ordered one. First look at a diagram for understanding.

Merge sort uses the idea of divide and conquer algorithm. Divide and conquer means divide and conquer, and break a big problem into a small problem to solve. The idea of divide and conquer algorithm is very similar to the recursion mentioned earlier. Divide and conquer algorithm is realized through recursion. Divide and conquer is an idea to solve problems, and recursion is a programming skill.

Let's look at the code

class Test{
   public void mergeSort(int[] a,int start,int end){
      if(start < end){
         int mid = (start + end)/2;
         mergeSort(a,start,mid);//Sort the left sequence
         mergeSort(a,mid + 1,end);//Sort the sequence on the right
         merge(a,start,mid,end);//merge
      }
   }
   
   public void merge(int[] a,int left,int mid,int right){
      //Set temporary array
      int[] temp = new int[a.length];
      //Set pointer
      int p1 = left;
      int p2 = mid + 1;
      int k = left;//This k points to the first position of the temp array
      while(p1 < mid && p2 < right){
         if(a[p1] < a[p2]){
            temp[k++] = a[p1++];
         } else {
            temp[k++] = a[p2++];
         }
      }
      //If there are still elements in the left sequence that have not been processed, they are directly added to the end
      while(p1 < mid) temp[k++] = a[p1++];
      while(p2 < right) temp[k++] = a[p2++];

      //Copy the element of temp back to a 
      for(int i : temp){
         a[i] = temp[i];
      }
   }

   @Test
   public void test(){
        int[] a = {2,3,4,5,6,2,4,5,7};
        mergeSort(a, 0, a.length-1);
        System.out.println("Ordered array:");
        for (int e : a)
            System.out.print(e+" ");
      }
   }
}

  Merge sort is a stable sort algorithm

Time complexity O(nlogn)

Spatial complexity O(n)

Quick sort

Principle and implementation of quick sort

Quick sorting also uses the idea of divide and conquer algorithm. Let's see the principle.

If you want to sort the data with subscripts from p to r in the array. Then, we select any data between p and r as the pivot (partition point), then traverse the data from p to r, put the data less than pivot on the left, the data greater than or equal to pivot on the right, and the pivot in the middle.

After processing, the data from p to r is divided into three parts. Assuming that the current location of pivot is q, the parts from p to q - 1 are smaller than pivot, and the parts from q + 1 to r are larger than pivot. As shown in the figure.

According to the divide and conquer processing idea, after the partition is completed, we recursively sort the data with subscripts from p to q - 1 and the data with subscripts from q+1 to r until the size of the interval to be sorted is reduced to 1, which shows that all the data in the array are in order, which is represented by a recursive formula below.

quick_sort(p,r) = partition(p,r) + quick_sort(p,q-1) + quick_sort(q+1,r).

Termination condition: P > = R

The following is written in pseudo code

quick_sort(A,n){
   //A represents the array and N represents the size
  quick_sort_c(A,0,n-1)
}

//Quick sort recursive function, P, R as subscript
quick_sort_c(A,p,r){
   if p >= r then return
   q = partition(A,p,r)//partition
   quick_sort(A,q+1,r)  
}

  The work of partition() is to randomly select an element as a pivot (generally select the last element in the interval from p to r), and then partition A[p,r] Based on the pivot. The partition function returns the pivot subscript after partition.

Let's take a look at the implementation of the in-situ partition function

partition(A,p,r){
   pivot := A[r]
   i := p
   for j := p to r do{
      if A[j] < pivot{
        swap A[i] with A[j]
        i := i + 1
      }
   }
   swap A[i] with A[j]
   return i;
}
The processing here is a bit similar to selection sorting. We divide A[p... r-1] into two parts by cursor i. Yuan of A[p... i-1]
Prime is less than pivot, we call it "processed interval" temporarily, and A[i... r-1] is "unprocessed interval". We
Each time, take an element A[j] from the unprocessed interval A[i... r-1] and compare it with pivot. If it is less than pivot, then
Add it to the tail of the processed interval, that is, the position of A[i].
Array insert operation, remember? Inserting elements at a certain position in the array requires moving data, which is very time-consuming. At that time we
It also talks about a processing technique, that is, exchange, and complete the insertion operation within the time complexity of O(1). Here we also use
This idea only needs to exchange A[i] and A[j], and A[j] can be subscripted as I within the O(1) time complexity
The location of the.
As shown in the figure:

The process of merging and sorting is Bottom to top , deal with the subproblems first, and then merge. And fast platoon is exactly the same
On the contrary, its processing process is From top to bottom , partition first, and then deal with the subproblem. Although merge sort is stable and time-consuming
The inter complexity is O(nlogn), but it is a non in situ sorting algorithm. As we said earlier, the reason for merging
This is a non in place sorting algorithm. The main reason is that the merge function cannot be executed in place. Quick sorting through ingenious in-situ sorting
Area function can realize in-situ sorting, which solves the problem that merging sorting occupies too much memory.

 

 

/**
     * Intra array element exchange
     * @param nums Input array
     * @param i Element 1 subscript
     * @param j Element 2 subscript
     */
private void swap(int[] nums, int i, int j) {
    int temp = nums [i];
    nums [i] = nums [j];
    nums [j] = temp;
}

/**
     * Quick sort
     *
     * @param nums Input array
     * @param left Divide left boundary
     * @param right Divide right boundary
     */
private void quickSort(int[] nums, int left, int right) {
    // Recursive return condition, and partition sorting end
    if (right-left <=0) {
        return;
    }
    // Select the right boundary value of the array as the partition node
    int pivot = nums[right];
    // Maintain partitions from the left bound of the array
    int partition=left;
    // Traverse the elements in the current partition
    for (int i = left; i < right-1; i++) {
        if ((nums [i] < pivot) ) {
            // Swap values less than pivot to the left of the partition
            // Swap values greater than or equal to pivot to the right of partition
            swap(nums, partition, i);
            partition++;
        }
    }
    // Insert the partition node into the middle of the left and right partitions of the array
    partition++;
    swap(nums, partition, right);
    // Partition node sorting completed
    // The left partition continues to sort and the right partition continues to sort
    quickSort(nums,left, partition-1);
    quickSort(nums,partition+1, right);
}

/**
     * Sort array entry function
     *
     * @param nums Input array
     * @return Returns the sorted array
     */
public int[] sortArray(int[] nums) {
    if (nums == null || nums.length ==0) {
        return nums;
    }
    quickSort(nums, 0, nums.length - 1);
    return nums;
}

Quick sort is not a stable sort algorithm

Time complexity O(nlogn)

Spatial complexity O(n)

Tags: Algorithm data structure

Posted on Tue, 28 Sep 2021 06:08:33 -0400 by jikishlove