Sorting algorithm of frequent interview tests

Numerical algorithms: solving equations, calculus, finite element analysis, signal processing, etc

Non numerical algorithms: sorting and searching

1, Bubble sort:

1. Algorithm:

9,7,5,3,1

Scan 1:     7,5,3,1,9

Scan 2:     5,3,1,7,9

...

Scan N-1:   1,3,5,7,9


  • (1) Compare adjacent elements, and if the first is larger than the second, swap the two of them

  • (2) Do the same for each pair of adjacent elements, from the first pair at the beginning to the last pair at the end. After this step, the last element will be the maximum value

  • (3) Repeat the above steps for all elements except the last one

  • (4) Continue to repeat the above steps for fewer and fewer elements each time until no elements need to be exchanged

example:
#include <stdio.h>

void bubble_sort(int data[], size_t size)
{
    size_t i;  // How many times
    for (i = 0; i < size - 1; ++i) {
        int ordered = 1;
        size_t j;  // How many times
        for (j = 0; j < size - 1 - i; ++j) {
            if (data[j+1] < data[j]) {
                data[j] = data[j] ^ data[j+1];
                data[j+1] = data[j] ^ data[j+1];
                data[j] = data[j] ^ data[j+1];
                ordered = 0;
            }
        }

        if (ordered) {
            break;
        }
    }
}

int main(void)
{
    int data[] = {9,0,7,2,5,4,3,6,1,8};
    size_t size = sizeof(data) / sizeof(data[0]);

    bubble_sort(data, size);

    size_t i;
    for (i = 0; i < size; ++i) {
            printf("%d ", data[i]);
    }
    printf("\n");
    return 0;
}

/*
Output:

0 1 2 3 4 5 6 7 8 9 

*/

2. Evaluation:

Average time complexity, O(N^2), stable, very sensitive to data ordering.

Stable means that the elements with the same data have the same order

Being sensitive to data means that the closer the data is to order, the shorter the consumption time, and the closer it is to reverse order, the longer the consumption time

2, Insert sort

1. Algorithm

  • (1) Starting with the first element, the element can be considered to have been sorted

  • (2) Take out the next element and scan from back to forward in the sorted element sequence

  • (3) If the element is larger than the new element, move the element to the next position

  • (4) If the element is less than or equal to the new element, the new element is inserted after the element

  • (5) Repeat step 2 until all elements are processed

example:
void insert_sort(int data[], size_t size)
{
    size_t i;
    for (i = 1; i < size; ++i) {
        int temp = data[i];
        size_t j;
        for (j = i; j - 1 >= 0; --j) {
            if (data[j-1] > temp) {
                data[j] = data[j-1];
            }
            else {
                data[j] = temp;
                break;
            }
        }
    }
}

2. Evaluation:

Average time complexity, O(N^2), stable, very sensitive to data ordering.

Bubble sort is value exchange, while insert sort is value movement, so insert sort is better than bubble sort.

3, Select sort

1. Algorithm:

First, find the smallest element in the unordered sequence and store it at the beginning of the sorting sequence. Then, continue to find the smallest element from the remaining unordered elements, and then put it at the end of the sorting sequence. And so on until all elements are sorted

example:
void select_sort(int data[], size_t size)
{
    size_t i;
    for (i = 0; i < size - 1; ++i) {
    	int k = i;
            for (int j = i + 1; j < size; ++j) {
                if (data[j] < data[k]) {
                    k = j;
                }
            }

            if (k != i) {
                data[i] = data[i] ^ data [k];
                data[k] = data[i] ^ data [k];
                data[i] = data[i] ^ data [k];
            }
    }
}

2. Evaluation:

Average time complexity, O(N^2), unstable.

Insensitive to data ordering. Although there are many comparisons, there is less data exchange, so it is generally faster than bubble sorting.

4, Quick sort

1. Algorithm:

  • (1) Select an element from the sequence to become the benchmark;

  • (2) Reorder the sequence. All elements smaller than the benchmark are placed in front of the benchmark, and all elements larger than the benchmark are placed behind the benchmark (the same elements can be placed on either side). This process is called partitioning

  • (3) Recursively sort element partitions smaller than the benchmark and element partitions larger than the benchmark

example:
void quick_sort(int data[], size_t left, size_t right)
{
    size_t p = (left + right) / 2;
    int pivot = data[p];
    size_t i = left, j = right;
    // i go right and j go left. When they coincide, they terminate
    while (i < j) {
        for (; !(i >= p || pivot < data[i]); ++i) {}  // In parentheses are the conditions for leaving the for loop
        if (i < p) {
            data[p] = data[i];
            p = i;
        }

        for (; !(j <= p || data[j] < pivot); --j) {}
        if (j > p) {
            data[p] = data[j];
            p = j;
        }
    }
    data[p] = pivot;

    if (p - left > 1) {
        quick_sort(data, left, p-1);
    }

    if (right - p > 1) {
        quick_sort(data, p + 1, right);
    }
}

2. Evaluation:

Average time complexity, O(NlogN), N*log2, logarithm of base N. Unstable. If the sequence can be divided equally each time, it is the fastest sorting algorithm.

The difference between the number greater than the reference value and the number less than the reference value is very small, and the less time is consumed.

Example 2:     Implementation of quick sorting method in C standard library
void quick_sort2(void* base, size_t left, size_t right, size_t size, 
	int(*compar)(const void*, const void*))
{
    // copy the size bytes of pb to pa
    // memcpy(pa, pb, size);
    // compar function pointer, - 1 means parameter 1 is less than parameter 2, 0 means equal, and 1 means parameter 1 is greater than parameter 2

    size_t p = (left + right) / 2;
    void* pivot = malloc(size);
    memcpy(pivot, base + p * size, size);
    size_t i = left, j = right;
    while (i < j) {
        for (; !(i >= p || compar(base+i*size, pivot) > 0); ++i) {}
        if (i < p) {
            memcpy(base+p*size, base+i*size, size);
            p = i;
        }

        for (; !(j <= p || compar(base+j*size, pivot) < 0); --j) {}
        if (j > p) {
            memcpy(base+p*size, base+j*size, size);
            p = j;
        }
    }
    
    memcpy(base + p * size, pivot, size);
    free(pivot);
    if (p - left > 1) {
    	quick_sort2(base, left, p - 1, size, compar);
    }

    if (right - p > 1) {
    	quick_sort2(base, p + 1, right, size, compar);
    }
}

void myqsort(void* base, size_t nmemb, size_t size, 
	int(*compar)(const void*, const void*))
{
    quick_sort2(base, 0, nmemb - 1, size, compar);
}

int main(void)
{
    int na[] = {55,23,65,2,4,75};
    size_t size = sizeof(na[0]);
    size_t numb = sizeof(na) / size;
    // qsort function of c standard library
    //qsort(na, numb, size, cmpint);
    myqsort(na, numb, size, cmpint);

    size_t i;
    for (i = 0; i < numb; ++i) {
        printf("%d ", na[i]);
    }
    printf("\n");

    const char* sa[] = {"daf", "ddeee", "rre", "awq", "bbfu"};
    size = sizeof(sa[0]);
    numb = sizeof(sa) / size;
    //qsort(sa, numb, size, cmpstr);
    myqsort(sa, numb, size, cmpstr);

    for (i = 0; i < numb; ++i) {
        printf("%s ", sa[i]);
    }
    printf("\n");

    return 0;
}

/*
Output:

2 4 23 55 65 75 
awq bbfu daf ddeee rre 

*/

5, Merge sort

1. Algorithm:

  • (1) Apply for space so that its size is the sum of two sorted sequences. The space is used to store the merged sequences

  • (2) Set two pointers. The initial position is the starting position of the two sorted sequences

  • (3) Compare the elements pointed to by the two pointers, select the relatively small element, put it into the merge space, and move the pointer to the next position

  • (4) Repeat step 3 until a pointer reaches the end of the sequence

  • (5) Copy all the remaining elements of another sequence directly to the end of the merged sequence

2. Evaluation:

Average time complexity, O(2NlogN). Stable and insensitive to data ordering. Non local sorting requires as much auxiliary space as the sequence to be sorted,

It is not suitable for sorting with large amount of data.

example:
// The functions that implement an external merge function, data1 and data2, are ordered
void outer_merge(int data1[], size_t size1, int data2[], size_t size2, int data3[]) {
    size_t i = 0, j = 0, k = 0;
    for (;;) {
        if (i < size1 && j < size2) {
            if (data1[i] < data2[j]) {
                data3[k++] = data1[i++];
            }
            else {
                data3[k++] = data2[j++];
            }
        }
        else if (i < size1) {
            data3[k++] = data1[i++];
        }
        else if (j < size2) {
            data3[k++] = data2[j++];
        }
        else {
            break;
        }
    }
}

// A function that implements an internal merge function, left to mid order, mid+1 to right order, and left to right order
void inner_merge(int data[], size_t left, size_t mid, size_t right) {
    size_t size = (right - left + 1) * sizeof(int);
    int* merge = malloc(size);

    outer_merge(data + left, 
        mid - left + 1,
        data + mid + 1,
        right - mid,
        merge);
    memcpy(data + left, merge, size);
    free(merge);
}

// Merge sort
void merge_sort(int data[], size_t left, size_t right) {
    if (left < right) {
        int mid = (left + right) / 2;
        merge_sort(data, left, mid);
        merge_sort(data, mid + 1, right);
        inner_merge(data, left, mid, right);
    }
}

Tags: Algorithm data structure Interview

Posted on Sat, 25 Sep 2021 22:20:54 -0400 by musicbase