| 
 | 1 | +/**  | 
 | 2 | + * @file  | 
 | 3 | + * @author [LJFP](https://github.com/ljfp/)  | 
 | 4 | + * @brief [Timsort](https://en.wikipedia.org/wiki/Timsort) implementation  | 
 | 5 | + * @details  | 
 | 6 | + * Timsort is a hybrid stable sorting algorithm, derived from merge sort and  | 
 | 7 | + * insertion sort, designed to perform well on many kinds of real-world data.  | 
 | 8 | + * Worst-case time complexity is O(n log n).  | 
 | 9 | + * Worst-case space complexity is O(n).  | 
 | 10 | + */  | 
 | 11 | +#include <assert.h>  | 
 | 12 | +#include <stdio.h>  | 
 | 13 | +#include <stdlib.h>  | 
 | 14 | +#include <time.h>  | 
 | 15 | + | 
 | 16 | +#define min(a, b) \  | 
 | 17 | +    ((a) < (b) ? (a) : (b)) /* Macro to find the minimum of two numbers */  | 
 | 18 | + | 
 | 19 | +/**  | 
 | 20 | + * @brief Performs insertion sort on a subarray  | 
 | 21 | + * @param arr Array to be sorted  | 
 | 22 | + * @param left Starting index of the subarray  | 
 | 23 | + * @param right Ending index of the subarray  | 
 | 24 | + */  | 
 | 25 | +void insertionSort(int arr[], int left, int right)  | 
 | 26 | +{  | 
 | 27 | +    for (int i = left + 1; i <= right; i++)  | 
 | 28 | +    {  | 
 | 29 | +        int temp = arr[i];  | 
 | 30 | +        int j = i - 1;  | 
 | 31 | +        while (j >= left && arr[j] > temp)  | 
 | 32 | +        {  | 
 | 33 | +            arr[j + 1] = arr[j];  | 
 | 34 | +            j--;  | 
 | 35 | +        }  | 
 | 36 | +        arr[j + 1] = temp;  | 
 | 37 | +    }  | 
 | 38 | +}  | 
 | 39 | + | 
 | 40 | +/**  | 
 | 41 | + * @brief Merges two sorted subarrays of arr[]  | 
 | 42 | + * @param arr The main array containing the subarrays to be merged  | 
 | 43 | + * @param l Left index of the first subarray  | 
 | 44 | + * @param m Ending index of the first subarray (midpoint)  | 
 | 45 | + * @param r Right index of the second subarray  | 
 | 46 | + */  | 
 | 47 | +void merge(int arr[], int l, int m, int r)  | 
 | 48 | +{  | 
 | 49 | +    int len1 = m - l + 1;  | 
 | 50 | +    int len2 = r - m;  | 
 | 51 | + | 
 | 52 | +    int *left = (int *)malloc(len1 * sizeof(int));  | 
 | 53 | +    int *right = (int *)malloc(len2 * sizeof(int));  | 
 | 54 | + | 
 | 55 | +    for (int i = 0; i < len1; i++) left[i] = arr[l + i];  | 
 | 56 | +    for (int i = 0; i < len2; i++) right[i] = arr[m + 1 + i];  | 
 | 57 | + | 
 | 58 | +    int i = 0, j = 0, k = l;  | 
 | 59 | + | 
 | 60 | +    /* Merging the two subarrays */  | 
 | 61 | +    while (i < len1 && j < len2)  | 
 | 62 | +    {  | 
 | 63 | +        if (left[i] <= right[j])  | 
 | 64 | +        {  | 
 | 65 | +            arr[k++] = left[i++];  | 
 | 66 | +        }  | 
 | 67 | +        else  | 
 | 68 | +        {  | 
 | 69 | +            arr[k++] = right[j++];  | 
 | 70 | +        }  | 
 | 71 | +    }  | 
 | 72 | + | 
 | 73 | +    /* Copying remaining elements of left[], if any */  | 
 | 74 | +    while (i < len1)  | 
 | 75 | +    {  | 
 | 76 | +        arr[k++] = left[i++];  | 
 | 77 | +    }  | 
 | 78 | + | 
 | 79 | +    /* Copying remaining elements of right[], if any */  | 
 | 80 | +    while (j < len2)  | 
 | 81 | +    {  | 
 | 82 | +        arr[k++] = right[j++];  | 
 | 83 | +    }  | 
 | 84 | + | 
 | 85 | +    free(left);  | 
 | 86 | +    free(right);  | 
 | 87 | +}  | 
 | 88 | + | 
 | 89 | +/**  | 
 | 90 | + * @brief Implementation of Timsort with specified run size  | 
 | 91 | + * @param arr Array to be sorted  | 
 | 92 | + * @param n Size of the array  | 
 | 93 | + * @param run_size Size of the runs. It should be a power of 2.  | 
 | 94 | + */  | 
 | 95 | +void timsort(int arr[], int n, int run_size)  | 
 | 96 | +{  | 
 | 97 | +    /* Sort individual subarrays of size run_size */  | 
 | 98 | +    for (int i = 0; i < n; i += run_size)  | 
 | 99 | +    {  | 
 | 100 | +        insertionSort(arr, i, min((i + run_size - 1), (n - 1)));  | 
 | 101 | +    }  | 
 | 102 | + | 
 | 103 | +    /* Start merging from size run_size */  | 
 | 104 | +    for (int size = run_size; size < n; size *= 2)  | 
 | 105 | +    {  | 
 | 106 | +        /* Pick starting point of left subarray */  | 
 | 107 | +        for (int left = 0; left < n; left += 2 * size)  | 
 | 108 | +        {  | 
 | 109 | +            /* Find ending point of left subarray */  | 
 | 110 | +            int mid = min(left + size - 1, n - 1);  | 
 | 111 | +            int right = min(left + 2 * size - 1, n - 1);  | 
 | 112 | + | 
 | 113 | +            /* Merge subarrays arr[left...mid] & arr[mid+1...right] */  | 
 | 114 | +            if (mid < right)  | 
 | 115 | +            {  | 
 | 116 | +                merge(arr, left, mid, right);  | 
 | 117 | +            }  | 
 | 118 | +        }  | 
 | 119 | +    }  | 
 | 120 | +}  | 
 | 121 | + | 
 | 122 | +/**  | 
 | 123 | + * @brief Test function to verify the Timsort implementation.  | 
 | 124 | + */  | 
 | 125 | +void test()  | 
 | 126 | +{  | 
 | 127 | +    const int size = rand() % 500; /* Random array size between 0 and 499 */  | 
 | 128 | +    int *arr = (int *)malloc(size * sizeof(int));  | 
 | 129 | + | 
 | 130 | +    /* Generate 'size' random numbers from -500 to 499 */  | 
 | 131 | +    for (int i = 0; i < size; i++)  | 
 | 132 | +    {  | 
 | 133 | +        arr[i] = (rand() % 1000) - 500; /* Signed random numbers */  | 
 | 134 | +    }  | 
 | 135 | + | 
 | 136 | +    /* Test with different run sizes */  | 
 | 137 | +    for (int run_size = 16; run_size <= 64; run_size *= 2)  | 
 | 138 | +    {  | 
 | 139 | +        /* Copy the original array to preserve it for each run size */  | 
 | 140 | +        int *arr_copy = (int *)malloc(size * sizeof(int));  | 
 | 141 | +        for (int i = 0; i < size; i++)  | 
 | 142 | +        {  | 
 | 143 | +            arr_copy[i] = arr[i];  | 
 | 144 | +        }  | 
 | 145 | + | 
 | 146 | +        timsort(arr_copy, size, run_size);  | 
 | 147 | + | 
 | 148 | +        /* Verify that the array is sorted */  | 
 | 149 | +        for (int i = 0; i < size - 1; ++i)  | 
 | 150 | +        {  | 
 | 151 | +            assert(arr_copy[i] <= arr_copy[i + 1]);  | 
 | 152 | +        }  | 
 | 153 | + | 
 | 154 | +        free(arr_copy);  | 
 | 155 | +    }  | 
 | 156 | + | 
 | 157 | +    free(arr);  | 
 | 158 | +}  | 
 | 159 | + | 
 | 160 | +/**  | 
 | 161 | + * @brief Main function to test Timsort implementation  | 
 | 162 | + * @return 0 on success  | 
 | 163 | + */  | 
 | 164 | +int main()  | 
 | 165 | +{  | 
 | 166 | +    /* Initialize random number generator */  | 
 | 167 | +    srand((unsigned int)time(NULL));  | 
 | 168 | + | 
 | 169 | +    /* Run the test multiple times */  | 
 | 170 | +    for (int i = 0; i < 1000; i++)  | 
 | 171 | +    {  | 
 | 172 | +        test();  | 
 | 173 | +    }  | 
 | 174 | + | 
 | 175 | +    printf("All tests passed successfully.\n");  | 
 | 176 | +    return 0;  | 
 | 177 | +}  | 
0 commit comments