C 语言十大排序算法

C 语言十大排序算法

物协算法培训 1 排序算法

1. 冒泡排序 (Bubble Sort)

  • 思想:通过相邻元素之间的比较和交换,每次将最大(或最小)的元素“冒泡”到数组的末尾。

  • 实现:使用嵌套循环,外层控制循环次数,内层进行相邻元素的比较和交换。

  • 时间复杂度:O(n^2)。即使是最优情况下,也需要进行完整的一轮比较。

    #include <stdio.h>
    
    void bubbleSort(int arr[], int n) {
        int i, j, temp;
        for (i = 0; i < n-1; i++) {
            // 每次循环都将最大的元素移到末尾
            for (j = 0; j < n-i-1; j++) {
                if (arr[j] > arr[j+1]) {
                    // 交换元素
                    temp = arr[j];
                    arr[j] = arr[j+1];
                    arr[j+1] = temp;
                }
            }
        }
    }
    
    int main() {
        int arr[] = {64, 34, 25, 12, 22, 11, 90};
        int n = sizeof(arr)/sizeof(arr[0]);
        bubbleSort(arr, n);
        printf("Sorted array: \n");
        for (int i=0; i < n; i++)
            printf("%d ", arr[i]);
        printf("\n");
        return 0;
    }
    

2. 选择排序 (Selection Sort)

  • 思想:每次选择未排序部分的最小元素,然后将其放置在已排序部分的末尾。

  • 实现:通过扫描未排序的部分找到最小的元素,并与未排序部分的第一个元素交换。

  • 时间复杂度:O(n^2)。在任何情况下,都需要进行 n(n-1)/2 次比较。

    #include <stdio.h>
    
    // 选择排序函数
    void selectionSort(int arr[], int n) {
        int i, j, minIndex, temp; // 声明循环和临时变量
    
        // 外层循环用于控制未排序部分的起始位置
        for (i = 0; i < n - 1; i++) {
            // 初始化未排序部分的最小元素索引为当前位置
            minIndex = i;
    
            // 内层循环用于寻找未排序部分的最小元素
            for (j = i + 1; j < n; j++) {
                if (arr[j] < arr[minIndex]) {
                    minIndex = j; // 更新最小元素的索引
                }
            }
    
            // 将未排序部分的最小元素交换到已排序部分的末尾
            temp = arr[minIndex];
            arr[minIndex] = arr[i];
            arr[i] = temp;
        }
    }
    
    int main() {
        int arr[] = {64, 25, 12, 22, 11}; // 给定的数组
        int n = sizeof(arr) / sizeof(arr[0]); // 计算数组的大小
        selectionSort(arr, n); // 调用选择排序函数对数组进行排序
        printf("Sorted array: \n");
        for (int i = 0; i < n; i++)
            printf("%d ", arr[i]); // 打印排序后的数组
        printf("\n");
        return 0;
    }
    
    
    

3. 插入排序 (Insertion Sort)

  • 思想:将数组视为已排序和未排序两部分,每次将未排序部分的元素插入到已排序部分的适当位置。

  • 实现:逐个将未排序部分的元素插入已排序部分的正确位置。

  • 时间复杂度:O(n^2)。最坏情况下,需要移动大量元素。

    #include <stdio.h>
    
    // 插入排序函数
    void insertionSort(int arr[], int n) {
        int i, key, j; // 声明循环和临时变量
    
        // 从数组的第二个元素开始,逐个将元素插入到已排序部分的正确位置
        for (i = 1; i < n; i++) {
            key = arr[i]; // 将当前元素存储在临时变量 key 中
            j = i - 1; // j 用于指向已排序部分的最后一个元素
    
            // 将比 key 大的元素向后移动,为 key 腾出插入的位置
            while (j >= 0 && arr[j] > key) {
                arr[j + 1] = arr[j]; // 向后移动元素
                j = j - 1; // 更新 j 的值,继续向前比较
            }
            arr[j + 1] = key; // 将 key 插入到正确的位置
        }
    }
    
    int main() {
        int arr[] = {12, 11, 13, 5, 6}; // 给定的数组
        int n = sizeof(arr) / sizeof(arr[0]); // 计算数组的大小
        insertionSort(arr, n); // 调用插入排序函数对数组进行排序
        printf("Sorted array: \n");
        for (int i = 0; i < n; i++)
            printf("%d ", arr[i]); // 打印排序后的数组
        printf("\n");
        return 0;
    }
    
    

4. 希尔排序 (Shell Sort)

  • 思想:是插入排序的一种改进版本,通过比较相隔一定间隔的元素,实现部分排序,最后再进行插入排序。

  • 实现:逐渐减小间隔,对每个间隔进行插入排序。

  • 时间复杂度:取决于间隔序列的选择。通常介于 O(n log n) 和 O(n^2) 之间。

    #include <stdio.h>
    
    // 希尔排序函数
    void shellSort(int arr[], int n) {
        int i, j, temp, gap; // 声明循环和临时变量
    
        // 初始时,将间隔(步长)设置为数组长度的一半,然后逐步缩小间隔
        for (gap = n / 2; gap > 0; gap /= 2) {
            // 对每个间隔进行插入排序
            for (i = gap; i < n; i++) {
                temp = arr[i]; // 将当前元素存储在临时变量中
                // 对当前间隔内的元素进行插入排序
                for (j = i; j >= gap && arr[j - gap] > temp; j -= gap) {
                    arr[j] = arr[j - gap]; // 向后移动元素
                }
                arr[j] = temp; // 将临时变量中存储的值放入正确的位置
            }
        }
    }
    
    int main() {
        int arr[] = {12, 34, 54, 2, 3}; // 给定的数组
        int n = sizeof(arr) / sizeof(arr[0]); // 计算数组的大小
        shellSort(arr, n); // 调用希尔排序函数对数组进行排序
        printf("Sorted array: \n");
        for (int i = 0; i < n; i++)
            printf("%d ", arr[i]); // 打印排序后的数组
        printf("\n");
        return 0;
    }
    
    
    

5. 归并排序 (Merge Sort)

  • 思想:采用分治法,将数组分为两半,分别排序,然后合并已排序的部分。

  • 实现:递归地将数组分成更小的数组,然后两两合并,直到整个数组有序。

  • 时间复杂度:始终为 O(n log n)。递归拆分和合并的复杂度均为 O(n log n)。

    #include <stdio.h>
    #include <stdlib.h>
    
    // 合并两个已排序的子数组
    void merge(int arr[], int left, int mid, int right) {
        int i, j, k;
        int n1 = mid - left + 1; // 左子数组的大小
        int n2 = right - mid;     // 右子数组的大小
    
        // 创建临时数组
        int L[n1], R[n2];
    
        // 将数据复制到临时数组 L 和 R
        for (i = 0; i < n1; i++)
            L[i] = arr[left + i];
        for (j = 0; j < n2; j++)
            R[j] = arr[mid + 1 + j];
    
        // 合并临时数组到 arr
        i = 0;
        j = 0;
        k = left;
        while (i < n1 && j < n2) {
            if (L[i] <= R[j]) {
                arr[k] = L[i];
                i++;
            } else {
                arr[k] = R[j];
                j++;
            }
            k++;
        }
    
        // 将剩余元素复制到 arr
        while (i < n1) {
            arr[k] = L[i];
            i++;
            k++;
        }
    
        while (j < n2) {
            arr[k] = R[j];
            j++;
            k++;
        }
    }
    
    // 归并排序
    void mergeSort(int arr[], int left, int right) {
        if (left < right) {
            // 找到中间点
            int mid = left + (right - left) / 2;
    
            // 对左右两部分递归进行归并排序
            mergeSort(arr, left, mid);
            mergeSort(arr, mid + 1, right);
    
            // 合并已排序的两部分
            merge(arr, left, mid, right);
        }
    }
    
    // 输出数组
    void printArray(int arr[], int size) {
        for (int i = 0; i < size; i++)
            printf("%d ", arr[i]);
        printf("\n");
    }
    
    int main() {
        int arr[] = {38, 27, 43, 3, 9, 82, 10}; // 给定的数组
        int arr_size = sizeof(arr) / sizeof(arr[0]); // 计算数组的大小
    
        printf("Given array is \n");
        printArray(arr, arr_size); // 打印原始数组
    
        mergeSort(arr, 0, arr_size - 1); // 调用归并排序函数对数组进行排序
    
        printf("\nSorted array is \n");
        printArray(arr, arr_size); // 打印排序后的数组
        return 0;
    }
    
    

6. 快速排序 (Quick Sort)

  • 思想:同样采用分治法,通过选定一个基准值,将数组分为比基准值小和比基准值大的两部分,然后递归地对这两部分进行排序。

  • 实现:选取基准值,分割数组,递归排序子数组,最后合并。

  • 时间复杂度:平均时间复杂度为 O(n log n),最坏情况为 O(n^2)。

    #include <stdio.h>
    
    // 交换元素
    void swap(int* a, int* b) {
        int t = *a;
        *a = *b;
        *b = t;
    }
    
    // 划分数组并返回划分点的索引
    int partition(int arr[], int low, int high) {
        int pivot = arr[high]; // 选择最后一个元素作为基准值
        int i = (low - 1); // 初始化较小元素的索引
    
        for (int j = low; j <= high - 1; j++) {
            // 如果当前元素小于或等于基准值
            if (arr[j] <= pivot) {
                i++; // 较小元素的索引加1
                swap(&arr[i], &arr[j]); // 交换 arr[i] 和 arr[j]
            }
        }
        swap(&arr[i + 1], &arr[high]); // 交换 arr[i + 1] 和 arr[high]
        return (i + 1); // 返回划分点的索引
    }
    
    // 快速排序函数
    void quickSort(int arr[], int low, int high) {
        if (low < high) {
            // 划分数组并获取划分点的索引
            int pi = partition(arr, low, high);
    
            // 对划分点的左侧和右侧子数组进行递归排序
            quickSort(arr, low, pi - 1);
            quickSort(arr, pi + 1, high);
        }
    }
    
    // 输出数组元素
    void printArray(int arr[], int size) {
        for (int i = 0; i < size; i++)
            printf("%d ", arr[i]);
        printf("\n");
    }
    
    int main() {
        int arr[] = {10, 7, 8, 9, 1, 5};
        int n = sizeof(arr) / sizeof(arr[0]);
        quickSort(arr, 0, n - 1);
        printf("Sorted array: \n");
        printArray(arr, n);
        return 0;
    }
    
    

7. 堆排序 (Heap Sort)

  • 思想:利用堆这种数据结构,将数组看作是完全二叉树,并且满足堆的性质(大顶堆或小顶堆)。

  • 实现:构建最大堆或最小堆,然后不断将堆顶元素与末尾元素交换,再重新调整堆。

  • 时间复杂度:O(n log n)。

    #include <stdio.h>
    
    // 堆调整函数,将以 root 为根的子树调整为最大堆
    void heapify(int arr[], int n, int root) {
        int largest = root; // 将根节点设置为最大值
        int left = 2 * root + 1; // 左子节点索引
        int right = 2 * root + 2; // 右子节点索引
    
        // 如果左子节点大于根节点
        if (left < n && arr[left] > arr[largest])
            largest = left;
    
        // 如果右子节点大于根节点
        if (right < n && arr[right] > arr[largest])
            largest = right;
    
        // 如果最大值不是根节点,交换根节点和最大值,并递归调整
        if (largest != root) {
            int temp = arr[root];
            arr[root] = arr[largest];
            arr[largest] = temp;
            // 递归调整
            heapify(arr, n, largest);
        }
    }
    
    // 堆排序函数
    void heapSort(int arr[], int n) {
        // 构建最大堆(从最后一个非叶子节点开始)
        for (int i = n / 2 - 1; i >= 0; i--)
            heapify(arr, n, i);
    
        // 逐步取出堆顶元素(最大值),并重新构建堆
        for (int i = n - 1; i > 0; i--) {
            // 将堆顶元素(最大值)与当前未排序部分的最后一个元素交换
            int temp = arr[0];
            arr[0] = arr[i];
            arr[i] = temp;
    
            // 重新构建堆
            heapify(arr, i, 0);
        }
    }
    
    // 输出数组元素
    void printArray(int arr[], int size) {
        for (int i = 0; i < size; i++)
            printf("%d ", arr[i]);
        printf("\n");
    }
    
    int main() {
        int arr[] = {12, 11, 13, 5, 6, 7};
        int n = sizeof(arr) / sizeof(arr[0]);
        heapSort(arr, n);
        printf("Sorted array: \n");
        printArray(arr, n);
        return 0;
    }
    
    

8. 计数排序 (Counting Sort)

  • 思想:适用于已知数据范围的整数排序,通过统计每个元素的出现次数,然后进行排序。

  • 实现:创建一个辅助数组来统计每个元素出现的次数,然后根据统计信息,将元素放置到正确的位置。

  • 时间复杂度:O(n + k),其中 k 是数据范围。

    #include <stdio.h>
    #include <stdlib.h>
    
    void countingSort(int arr[], int n, int range) {
        int output[n]; // 用于存储排序后的数组
        int count[range + 1]; // 计数数组,存储每个元素的出现次数
    
        // 将计数数组的所有元素初始化为 0
        for (int i = 0; i <= range; ++i)
            count[i] = 0;
    
        // 统计每个元素出现的次数
        for (int i = 0; i < n; ++i)
            ++count[arr[i]];
    
        // 修改计数数组,使其存储元素的最终位置
        for (int i = 1; i <= range; ++i)
            count[i] += count[i - 1];
    
        // 将元素放置到 output 数组中
        for (int i = n - 1; i >= 0; --i) {
            output[count[arr[i]] - 1] = arr[i];
            --count[arr[i]];
        }
    
        // 将 output 数组复制到原数组中,完成排序
        for (int i = 0; i < n; ++i)
            arr[i] = output[i];
    }
    
    // 输出数组元素
    void printArray(int arr[], int size) {
        for (int i = 0; i < size; ++i)
            printf("%d ", arr[i]);
        printf("\n");
    }
    
    int main() {
        int arr[] = {4, 2, 2, 8, 3, 3, 1};
        int n = sizeof(arr) / sizeof(arr[0]);
        int range = 8; // 范围为 0 到 8 的整数
    
        printf("Original array: \n");
        printArray(arr, n);
    
        countingSort(arr, n, range);
    
        printf("Sorted array: \n");
        printArray(arr, n);
        return 0;
    }
    
    

9. 桶排序 (Bucket Sort)

  • 思想:将数组划分为若干个桶,每个桶内部再进行排序,然后将各个桶中的元素合并得到最终的排序结果。

  • 实现:确定桶的数量和范围,将元素分配到对应的桶中,然后对每个桶内的元素进行排序。

  • 时间复杂度:取决于桶的数量和内部排序算法。

    #include <stdio.h>
    #include <stdlib.h>
    
    // 桶排序中的桶数据结构
    struct Node {
        int data;
        struct Node* next;
    };
    
    // 插入排序,用于桶内部排序
    void insertionSort(struct Node* bucket) {
        struct Node* i, *j;
        for (i = bucket->next; i != NULL; i = i->next) {
            int temp = i->data;
            for (j = i->next; j != NULL && j->data < temp; j = j->next)
                j->next->data = j->data;
            j->data = temp;
        }
    }
    
    // 桶排序
    void bucketSort(int arr[], int n, int max_value) {
        int i, j;
        // 创建桶数组,其中每个桶用链表来实现
        struct Node* buckets[max_value];
    
        // 初始化桶数组
        for (i = 0; i < max_value; i++)
            buckets[i] = NULL;
    
        // 将数组元素分配到对应的桶中
        for (i = 0; i < n; i++) {
            struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
            newNode->data = arr[i];
            newNode->next = NULL;
    
            int index = arr[i] / max_value;
            if (buckets[index] == NULL)
                buckets[index] = newNode;
            else {
                struct Node* temp = buckets[index];
                while (temp->next != NULL)
                    temp = temp->next;
                temp->next = newNode;
            }
        }
    
        // 对每个桶进行插入排序
        for (i = 0; i < max_value; i++) {
            if (buckets[i] != NULL)
                insertionSort(buckets[i]);
        }
    
        // 将排序后的元素放回原数组
        int index = 0;
        for (i = 0; i < max_value; i++) {
            struct Node* temp = buckets[i];
            while (temp != NULL) {
                arr[index++] = temp->data;
                temp = temp->next;
            }
        }
    
        // 释放动态分配的内存
        for (i = 0; i < max_value; i++) {
            struct Node* temp = buckets[i];
            while (temp != NULL) {
                struct Node* prev = temp;
                temp = temp->next;
                free(prev);
            }
        }
    }
    
    // 输出数组元素
    void printArray(int arr[], int size) {
        for (int i = 0; i < size; i++)
            printf("%d ", arr[i]);
        printf("\n");
    }
    
    int main() {
        int arr[] = {29, 25, 3, 49, 9, 37, 21, 43}; // 给定的数组
        int n = sizeof(arr) / sizeof(arr[0]); // 计算数组的大小
        int max_value = 50; // 最大值范围
    
        printf("Original array: \n");
        printArray(arr, n); // 打印原始数组
    
        bucketSort(arr, n, max_value); // 调用桶排序函数对数组进行排序
    
        printf("Sorted array: \n");
        printArray(arr, n); // 打印排序后的数组
        return 0;
    }
    
    

10. 基数排序 (Radix Sort)

  • 思想:按照位数进行排序,从最低有效位(个位)到最高有效位(最高位),每个位上进行稳定排序。

  • 实现:根据位数进行排序,可以借助计数排序等稳定排序算法。

  • 时间复杂度:O(d * (n + k)),其中 d 是数字的最大位数,k 是基数(常数)。

    #include <stdio.h>
    
    // 获取数组中的最大值
    int getMax(int arr[], int n) {
        int max = arr[0];
        for (int i = 1; i < n; i++) {
            if (arr[i] > max)
                max = arr[i];
        }
        return max;
    }
    
    // 对数组按照指定位数进行计数排序(基数排序中的一步)
    void countSort(int arr[], int n, int exp) {
        int output[n];
        int count[10] = {0};
    
        // 统计每个数字出现的次数
        for (int i = 0; i < n; i++)
            count[(arr[i] / exp) % 10]++;
    
        // 修改 count[i],使其包含小于等于 i 的元素个数
        for (int i = 1; i < 10; i++)
            count[i] += count[i - 1];
    
        // 从原数组中构建输出数组
        for (int i = n - 1; i >= 0; i--) {
            output[count[(arr[i] / exp) % 10] - 1] = arr[i];
            count[(arr[i] / exp) % 10]--;
        }
    
        // 将输出数组复制到原数组中
        for (int i = 0; i < n; i++)
            arr[i] = output[i];
    }
    
    // 基数排序
    void radixSort(int arr[], int n) {
        int max = getMax(arr, n); // 获取数组中的最大值
    
        // 对每一位进行计数排序,exp 表示位数,从个位到最高位
        for (int exp = 1; max / exp > 0; exp *= 10)
            countSort(arr, n, exp);
    }
    
    // 输出数组元素
    void printArray(int arr[], int size) {
        for (int i = 0; i < size; i++)
            printf("%d ", arr[i]);
        printf("\n");
    }
    
    int main() {
        int arr[] = {170, 45, 75, 90, 802, 24, 2, 66};
        int n = sizeof(arr) / sizeof(arr[0]);
    
        printf("Original array: \n");
        printArray(arr, n);
    
        radixSort(arr, n);
    
        printf("Sorted array: \n");
        printArray(arr, n);
        return 0;
    }
    
    
  • 18
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值