算法筑基(一):排序算法——从冒泡到快排,一文掌握最经典的排序算法

算法筑基(一):排序算法------从冒泡到快排,一文掌握最经典的排序算法


📖 前言

排序是计算机科学中最基础也最重要的算法之一。无论你是做数据分析、写数据库引擎,还是刷LeetCode,排序都会频繁出现。一个好的排序算法,能让你的程序从"能跑"变成"跑得快"。

在本文中,我们将从最直观的冒泡排序开始,逐步深入到选择、插入、希尔、归并、快速、堆排序,最后介绍几种非比较排序(计数排序、基数排序、桶排序)。每一个算法都会配完整的C语言代码逐行注释 以及实际案例,确保你不仅能理解原理,还能亲手实现。


📌 本文目录

  1. 冒泡排序 ------ 像气泡一样上浮
  2. 选择排序 ------ 每次选最小的
  3. 插入排序 ------ 像打牌一样插入
  4. 希尔排序 ------ 插入排序的升级版
  5. 归并排序 ------ 分治思想的典范
  6. 快速排序 ------ 工业级的排序利器
  7. 堆排序 ------ 利用堆结构的选择排序
  8. 非比较排序 ------ 计数排序 / 基数排序 / 桶排序
  9. 排序算法对比总结
  10. 课后练习与答案

1. 冒泡排序 ------ 像气泡一样上浮

1.1 核心思想

重复遍历数组,每次比较相邻两个元素,如果顺序错误就交换。每一轮遍历都会把当前未排序部分的最大(或最小)元素"冒泡"到正确位置。

1.2 C语言实现(带详细注释)

c 复制代码
#include <stdio.h>

// 冒泡排序函数
void bubbleSort(int arr[], int n) {
    int i, j, temp;
    // 外层循环控制轮数,每轮确定一个最大值
    for (i = 0; i < n - 1; i++) {
        // 优化标志:如果本轮没有发生交换,说明数组已有序
        int swapped = 0;
        // 内层循环进行相邻比较,n-i-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;
                swapped = 1;
            }
        }
        // 如果未发生交换,提前结束
        if (!swapped)
            break;
    }
}

// 打印数组
void printArray(int arr[], int n) {
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

int main() {
    int arr[] = {64, 34, 25, 12, 22, 11, 90};
    int n = sizeof(arr) / sizeof(arr[0]);

    printf("原始数组: ");
    printArray(arr, n);

    bubbleSort(arr, n);

    printf("排序后数组: ");
    printArray(arr, n);

    return 0;
}

1.3 案例:学生成绩排名

假设有一个班级的C语言考试成绩,老师希望按照分数从低到高排序,以便查看及格线。冒泡排序可以轻松完成。

c 复制代码
// 学生成绩数组
int scores[] = {78, 45, 92, 60, 88, 73};
bubbleSort(scores, 6);
// 输出:45 60 73 78 88 92

1.4 复杂度分析

  • 最好情况:数组已经有序,只需比较 n-1 次,复杂度 O(n)。
  • 最坏情况:数组逆序,比较次数为 n(n-1)/2,复杂度 O(n²)。
  • 空间复杂度:O(1),原地排序。

1.5 小思考

冒泡排序虽然慢,但它是稳定的排序(相等元素相对位置不变),且容易实现。适合小规模数据或教学演示。


2. 选择排序 ------ 每次选最小的

2.1 核心思想

每次从未排序部分选出最小(或最大)的元素,放到已排序部分的末尾。依次重复,直到全部有序。

2.2 C语言实现

c 复制代码
void selectionSort(int arr[], int n) {
    int i, j, minIdx, temp;
    for (i = 0; i < n - 1; i++) {
        // 假设当前元素是最小值
        minIdx = i;
        // 在未排序部分找真正的最小值下标
        for (j = i + 1; j < n; j++) {
            if (arr[j] < arr[minIdx]) {
                minIdx = j;
            }
        }
        // 如果最小值不是当前元素,则交换
        if (minIdx != i) {
            temp = arr[i];
            arr[i] = arr[minIdx];
            arr[minIdx] = temp;
        }
    }
}

2.3 案例:扑克牌整理

你手中有一把乱序的扑克牌,你想把它们从小到大排列。选择排序就像你每次从剩下牌中找出最小的一张,放到已排好序的牌堆后面。

2.4 复杂度分析

  • 无论输入顺序如何,比较次数都是 n(n-1)/2,所以最好、最坏、平均都是 O(n²)。
  • 空间复杂度 O(1)。
  • 不稳定:交换可能会打乱相等元素的相对顺序。

3. 插入排序 ------ 像打牌一样插入

3.1 核心思想

将数组分成已排序和未排序两部分,每次取未排序部分的第一个元素,插入到已排序部分的正确位置。

3.2 C语言实现

c 复制代码
void insertionSort(int arr[], int n) {
    int i, j, key;
    for (i = 1; i < n; i++) {
        key = arr[i];   // 待插入元素
        j = i - 1;
        // 将比 key 大的元素向后移动
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = key;   // 插入到正确位置
    }
}

3.3 案例:图书馆还书

图书管理员把归还的书籍按索书号插入到书架正确位置。如果书架基本有序,插入排序效率极高。

3.4 复杂度分析

  • 最好情况:数组已有序,只需比较 n-1 次,O(n)。
  • 最坏情况:逆序,需要比较 n(n-1)/2 次,O(n²)。
  • 平均 O(n²)。
  • 空间 O(1),稳定排序。

3.5 小贴士

插入排序对于小规模或基本有序的数据非常高效,常常作为高级排序算法(如快速排序)的"小数组优化"部分。


4. 希尔排序 ------ 插入排序的升级版

4.1 核心思想

希尔排序是插入排序的一种改进,通过将整个序列分割成若干子序列分别进行插入排序,使得整个序列基本有序,最后再对整个序列进行一次插入排序。它利用了插入排序在数据基本有序时效率极高的特点。

4.2 算法步骤

  1. 选择一个增量序列(通常使用希尔增量:n/2, n/4, ..., 1)。
  2. 对每个增量 gap,将数组分成若干间隔为 gap 的子序列,对每个子序列进行插入排序。
  3. 逐步缩小 gap,直到 gap = 1 时进行最后一次插入排序。

4.3 C语言实现

c 复制代码
void shellSort(int arr[], int n) {
    // 初始增量 gap = n/2,每次减半,直到 gap = 1
    for (int gap = n / 2; gap > 0; gap /= 2) {
        // 对每个子序列进行插入排序
        for (int i = gap; i < n; i++) {
            int temp = arr[i];
            int j;
            // 插入排序:将 arr[i] 插入到子序列的正确位置
            for (j = i; j >= gap && arr[j - gap] > temp; j -= gap) {
                arr[j] = arr[j - gap];
            }
            arr[j] = temp;
        }
    }
}

4.4 案例:大规模数据初步整理

在处理海量数据时,先使用希尔排序将数据变得基本有序,再用插入排序快速完成,能大幅提升效率。

4.5 复杂度分析

  • 希尔排序的时间复杂度取决于增量序列的选择。使用希尔增量时,最坏情况 O(n²);使用 Hibbard 增量(1,3,7,15,...)时,最坏 O(n^(3/2));使用 Sedgewick 增量时,最坏 O(n^(4/3))。
  • 空间复杂度 O(1)。
  • 不稳定

5. 归并排序 ------ 分治思想的典范

5.1 核心思想

采用分治法

  1. 分解:将数组分成两半。
  2. 解决:递归地对左右两半进行归并排序。
  3. 合并:将两个有序子数组合并成一个有序数组。

5.2 C语言实现(含合并函数)

c 复制代码
// 合并两个有序子数组 [l, m] 和 [m+1, r]
void merge(int arr[], int l, int m, int r) {
    int i, j, k;
    int n1 = m - l + 1;
    int n2 = r - m;

    // 创建临时数组
    int L[n1], R[n2];

    // 拷贝数据到临时数组
    for (i = 0; i < n1; i++)
        L[i] = arr[l + i];
    for (j = 0; j < n2; j++)
        R[j] = arr[m + 1 + j];

    // 合并临时数组回 arr[l..r]
    i = 0; j = 0; k = l;
    while (i < n1 && j < n2) {
        if (L[i] <= R[j]) {
            arr[k] = L[i];
            i++;
        } else {
            arr[k] = R[j];
            j++;
        }
        k++;
    }

    // 拷贝剩余元素
    while (i < n1) {
        arr[k] = L[i];
        i++; k++;
    }
    while (j < n2) {
        arr[k] = R[j];
        j++; k++;
    }
}

// 归并排序主函数
void mergeSort(int arr[], int l, int r) {
    if (l < r) {
        int m = l + (r - l) / 2;  // 防止溢出
        mergeSort(arr, l, m);
        mergeSort(arr, m + 1, r);
        merge(arr, l, m, r);
    }
}

5.3 案例:大文件外部排序

当内存无法一次容纳全部数据时,归并排序常用于外部排序:将文件分成小块排序,再合并。

5.4 复杂度分析

  • 时间复杂度:始终为 O(n log n)。
  • 空间复杂度:O(n),因为需要临时数组。
  • 稳定排序。

5.5 优点与缺点

  • 优点:稳定、性能稳定(不依赖输入顺序)。
  • 缺点:需要额外空间,对于小数组递归开销较大。

6. 快速排序 ------ 工业级的排序利器

6.1 核心思想

也是分治法,但比归并排序更"聪明":

  1. 选取一个基准(pivot)。
  2. 将数组划分为两部分,左边都小于等于基准,右边都大于等于基准。
  3. 递归地对左右两部分进行快速排序。

6.2 C语言实现(经典写法)

c 复制代码
// 划分函数:返回基准的最终位置
int partition(int arr[], int low, int high) {
    int pivot = arr[high];   // 选取最后一个元素为基准
    int i = low - 1;         // i 指向小于基准的最后一个元素

    for (int j = low; j < high; j++) {
        // 如果当前元素小于等于基准,则将其交换到左侧
        if (arr[j] <= pivot) {
            i++;
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
    }
    // 将基准放到正确位置
    int temp = arr[i + 1];
    arr[i + 1] = arr[high];
    arr[high] = temp;
    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);
    }
}

6.3 案例:数据库索引排序

数据库在创建索引时,常使用快速排序或其变种(如三路快排)来快速建立有序索引。

6.4 复杂度分析

  • 平均 O(n log n),实际表现非常优秀。
  • 最坏 O(n²)(当每次基准都是最大或最小值时,如已有序数组)。
  • 空间复杂度 O(log n)(递归栈深度)。
  • 不稳定

6.5 优化技巧

  • 随机选择基准,避免最坏情况。
  • 当子数组较小时,改用插入排序。
  • 三路快排(处理大量重复元素)。

7. 堆排序 ------ 利用堆结构的选择排序

7.1 核心思想

堆排序利用堆这种数据结构(通常用数组实现)来进行排序。首先将待排序序列构造成一个最大堆(或最小堆),此时堆顶元素就是最大值(或最小值),将其与堆尾元素交换,然后对剩余元素重新调整成堆,重复该过程直到全部有序。

7.2 算法步骤

  1. 建堆:从最后一个非叶子节点开始,自底向上调整,将数组调整为最大堆。
  2. 排序:每次将堆顶元素(最大值)与堆尾元素交换,堆大小减1,然后对堆顶进行向下调整,重复直到堆大小为1。

7.3 C语言实现

c 复制代码
// 调整以 root 为根的子树为最大堆,n 为堆的大小
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);
    }
}

7.4 案例:优先队列的实现

堆排序是堆的典型应用,可用于实现优先队列,在操作系统调度、图算法(如Dijkstra)中广泛应用。

7.5 复杂度分析

  • 时间复杂度:建堆 O(n),每次调整 O(log n),共 n 次,总 O(n log n)。
  • 空间复杂度:O(1)。
  • 不稳定

8. 非比较排序 ------ 计数排序 / 基数排序 / 桶排序

比较排序的下界是 O(n log n),而非比较排序可以突破这一限制,但通常需要满足特定条件(如数据范围有限)。

8.1 计数排序

8.1.1 核心思想

计数排序适用于数据范围不大的整数排序。通过统计每个值出现的次数,然后根据次数直接输出有序序列。

8.1.2 C语言实现
c 复制代码
void countingSort(int arr[], int n) {
    // 先找出数组中的最大值
    int max = arr[0];
    for (int i = 1; i < n; i++) {
        if (arr[i] > max) max = arr[i];
    }

    // 创建计数数组并初始化为0
    int count[max + 1];
    for (int i = 0; i <= max; i++) count[i] = 0;

    // 统计每个元素出现的次数
    for (int i = 0; i < n; i++) {
        count[arr[i]]++;
    }

    // 根据计数数组重建原数组
    int index = 0;
    for (int i = 0; i <= max; i++) {
        while (count[i]-- > 0) {
            arr[index++] = i;
        }
    }
}
8.1.3 复杂度
  • 时间复杂度:O(n + k),k 为数据范围。
  • 空间复杂度:O(k)。
  • 稳定(如果实现时使用稳定累加方式)。

8.2 基数排序

8.2.1 核心思想

基数排序按照低位到高位的顺序,依次对每一位进行稳定的计数排序(或桶排序)。适用于整数或字符串。

8.2.2 C语言实现(基于计数排序)
c 复制代码
// 使用计数排序对 arr 按照第 exp 位(10^exp)进行排序
void countingSortByDigit(int arr[], int n, int exp) {
    int output[n];       // 临时存放排序结果
    int count[10] = {0}; // 数字 0~9

    // 统计当前位上的数字出现次数
    for (int i = 0; i < n; i++) {
        int digit = (arr[i] / exp) % 10;
        count[digit]++;
    }

    // 计算累积次数(用于稳定排序)
    for (int i = 1; i < 10; i++) {
        count[i] += count[i - 1];
    }

    // 从后向前构建输出数组(保证稳定性)
    for (int i = n - 1; i >= 0; i--) {
        int digit = (arr[i] / exp) % 10;
        output[count[digit] - 1] = arr[i];
        count[digit]--;
    }

    // 将排好序的数据复制回原数组
    for (int i = 0; i < n; i++) {
        arr[i] = output[i];
    }
}

void radixSort(int arr[], int n) {
    // 找出最大值,确定最大位数
    int max = arr[0];
    for (int i = 1; i < n; i++) {
        if (arr[i] > max) max = arr[i];
    }

    // 从个位开始,对每一位进行排序
    for (int exp = 1; max / exp > 0; exp *= 10) {
        countingSortByDigit(arr, n, exp);
    }
}
8.2.3 复杂度
  • 时间复杂度:O(d * (n + k)),d 为最大位数,k 为基数(这里为10)。
  • 空间复杂度:O(n + k)。
  • 稳定

8.3 桶排序

8.3.1 核心思想

桶排序将数据分到若干个桶中,每个桶内单独排序(通常使用插入排序或快速排序),最后按顺序合并所有桶。

8.3.2 C语言实现(简单版本,假设数据在 [0,1) 区间)
c 复制代码
#include <stdlib.h>

#define BUCKET_SIZE 10

// 桶内插入排序(也可以使用其他排序)
void bucketInsertSort(float bucket[], int *size) {
    for (int i = 1; i < *size; i++) {
        float key = bucket[i];
        int j = i - 1;
        while (j >= 0 && bucket[j] > key) {
            bucket[j + 1] = bucket[j];
            j--;
        }
        bucket[j + 1] = key;
    }
}

void bucketSort(float arr[], int n) {
    // 创建桶数组
    float *buckets[BUCKET_SIZE];
    int bucketSizes[BUCKET_SIZE] = {0};

    // 为每个桶分配足够空间(最多 n 个元素)
    for (int i = 0; i < BUCKET_SIZE; i++) {
        buckets[i] = (float*)malloc(n * sizeof(float));
    }

    // 将元素分配到桶中(假设数据在 [0,1) 区间)
    for (int i = 0; i < n; i++) {
        int idx = (int)(arr[i] * BUCKET_SIZE);
        if (idx >= BUCKET_SIZE) idx = BUCKET_SIZE - 1;
        buckets[idx][bucketSizes[idx]++] = arr[i];
    }

    // 对每个桶内部排序
    for (int i = 0; i < BUCKET_SIZE; i++) {
        if (bucketSizes[i] > 0) {
            bucketInsertSort(buckets[i], &bucketSizes[i]);
        }
    }

    // 合并所有桶
    int index = 0;
    for (int i = 0; i < BUCKET_SIZE; i++) {
        for (int j = 0; j < bucketSizes[i]; j++) {
            arr[index++] = buckets[i][j];
        }
        free(buckets[i]);
    }
}
8.3.3 复杂度
  • 时间复杂度:平均 O(n + k)(k 为桶数),最坏 O(n²)(所有元素进入同一个桶)。
  • 空间复杂度:O(n + k)。
  • 稳定(取决于桶内排序算法)。

📊 排序算法对比总结

算法 最好时间复杂度 平均时间复杂度 最坏时间复杂度 空间复杂度 稳定性
冒泡排序 O(n) O(n²) O(n²) O(1) 稳定
选择排序 O(n²) O(n²) O(n²) O(1) 不稳定
插入排序 O(n) O(n²) O(n²) O(1) 稳定
希尔排序 O(n log n)~O(n^(4/3)) O(n(3/2))~O(n(4/3)) O(n²) O(1) 不稳定
归并排序 O(n log n) O(n log n) O(n log n) O(n) 稳定
快速排序 O(n log n) O(n log n) O(n²) O(log n) 不稳定
堆排序 O(n log n) O(n log n) O(n log n) O(1) 不稳定
计数排序 O(n+k) O(n+k) O(n+k) O(k) 稳定
基数排序 O(d(n+k)) O(d(n+k)) O(d(n+k)) O(n+k) 稳定
桶排序 O(n+k) O(n+k) O(n²) O(n+k) 稳定

🎯 如何选择排序算法?

  • 数据量小:插入排序、冒泡排序简单易懂。
  • 数据基本有序:插入排序表现极佳。
  • 要求稳定排序:归并排序、冒泡排序、插入排序。
  • 追求速度且内存充足:归并排序。
  • 通用高效排序:快速排序(平均最快)。
  • 数据范围有限且为整数:计数排序、基数排序。
  • 数据分布均匀:桶排序。

✍️ 课后练习与答案

练习题目

  1. 实现一个双向冒泡排序(鸡尾酒排序),提高基本有序数组的效率。
  2. 修改快速排序,加入随机基准选择,并测试对已有序数组的排序速度。
  3. 用归并排序实现一个统计逆序对数量的函数。
  4. 尝试用C语言实现一个"万能排序函数",通过函数指针支持任意类型比较。
  5. 使用计数排序对一组范围在 0~100 的整数进行排序。
  6. 使用基数排序对一组三位数整数进行排序。

参考答案

1. 双向冒泡排序(鸡尾酒排序)
c 复制代码
void cocktailSort(int arr[], int n) {
    int swapped = 1;
    int start = 0, end = n - 1;
    while (swapped) {
        swapped = 0;
        // 从左向右冒泡,将最大值移到末尾
        for (int i = start; i < end; i++) {
            if (arr[i] > arr[i + 1]) {
                int temp = arr[i];
                arr[i] = arr[i + 1];
                arr[i + 1] = temp;
                swapped = 1;
            }
        }
        if (!swapped) break;
        end--;

        // 从右向左冒泡,将最小值移到开头
        for (int i = end - 1; i >= start; i--) {
            if (arr[i] > arr[i + 1]) {
                int temp = arr[i];
                arr[i] = arr[i + 1];
                arr[i + 1] = temp;
                swapped = 1;
            }
        }
        start++;
    }
}
2. 随机快速排序
c 复制代码
#include <stdlib.h>
#include <time.h>

int randomPartition(int arr[], int low, int high) {
    // 随机选择一个下标作为基准
    int randomIdx = low + rand() % (high - low + 1);
    // 将基准交换到末尾
    int temp = arr[randomIdx];
    arr[randomIdx] = arr[high];
    arr[high] = temp;
    // 调用原来的划分函数
    return partition(arr, low, high);
}

void randomQuickSort(int arr[], int low, int high) {
    if (low < high) {
        int pi = randomPartition(arr, low, high);
        randomQuickSort(arr, low, pi - 1);
        randomQuickSort(arr, pi + 1, high);
    }
}
3. 归并排序统计逆序对
c 复制代码
long long mergeAndCount(int arr[], int l, int m, int r) {
    int n1 = m - l + 1;
    int n2 = r - m;
    int L[n1], R[n2];
    for (int i = 0; i < n1; i++) L[i] = arr[l + i];
    for (int j = 0; j < n2; j++) R[j] = arr[m + 1 + j];

    long long invCount = 0;
    int i = 0, j = 0, k = l;
    while (i < n1 && j < n2) {
        if (L[i] <= R[j]) {
            arr[k++] = L[i++];
        } else {
            arr[k++] = R[j++];
            invCount += (n1 - i); // 所有剩余的 L[i..n1-1] 都与 R[j-1] 形成逆序
        }
    }
    while (i < n1) arr[k++] = L[i++];
    while (j < n2) arr[k++] = R[j++];
    return invCount;
}

long long mergeSortAndCount(int arr[], int l, int r) {
    long long invCount = 0;
    if (l < r) {
        int m = l + (r - l) / 2;
        invCount += mergeSortAndCount(arr, l, m);
        invCount += mergeSortAndCount(arr, m + 1, r);
        invCount += mergeAndCount(arr, l, m, r);
    }
    return invCount;
}
4. 万能排序函数(以整型为例,实际可用 void* 和比较函数指针)
c 复制代码
typedef int (*cmpFunc)(int, int);

void genericSort(int arr[], int n, cmpFunc cmp) {
    // 使用冒泡排序演示(可替换为任何排序)
    for (int i = 0; i < n - 1; i++) {
        int swapped = 0;
        for (int j = 0; j < n - i - 1; j++) {
            if (cmp(arr[j], arr[j + 1]) > 0) {
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
                swapped = 1;
            }
        }
        if (!swapped) break;
    }
}

// 使用示例:升序
int ascending(int a, int b) { return a - b; }
// 降序
int descending(int a, int b) { return b - a; }
5. 计数排序(范围 0~100)

直接使用前面给出的 countingSort 函数即可,注意要找出 max 值(100)。

6. 基数排序(三位数整数)

直接使用前面给出的 radixSort 函数即可,因为最大三位数,循环三次(个位、十位、百位)。


🌟 寄语

排序算法是算法世界的"敲门砖"。当你亲手把冒泡、快排这些代码敲出来,看着控制台输出从乱序变为整齐的那一刻,你会发现算法并没有想象中那么高深。

不要只看不练------打开你的C语言环境,把上面的代码一个一个跑起来,修改参数,观察输出。甚至可以尝试用打印语句追踪每一步的执行过程,你会对算法有更深的理解。

在下一篇文章中,我们将进入搜索算法的世界,从最简单的线性查找,到二分查找,再到DFS/BFS这些图搜索的基石。敬请期待!


如果你在阅读过程中有任何疑问,欢迎在评论区留言,我会尽快回复。
如果你觉得这篇文章对你有帮助,点个赞👍,让更多同学看到。

相关推荐
qwehjk20082 小时前
代码动态生成技术
开发语言·c++·算法
承渊政道2 小时前
【优选算法】(实战体会位运算的逻辑思维)
数据结构·c++·笔记·学习·算法·leetcode·visual studio
Frostnova丶2 小时前
LeetCode 2573. 找出对应 LCP 矩阵的字符串
算法·leetcode·矩阵
承渊政道3 小时前
【优选算法】(实战推演模拟算法的蕴含深意)
数据结构·c++·笔记·学习·算法·leetcode·排序算法
林鸿群3 小时前
实现支持纳秒级精度的时间引擎(C++)
算法·定时引擎
Keep learning!3 小时前
PCA主成分分析学习
学习·算法
专注VB编程开发20年3 小时前
CUDA实现随机切割算法,显卡多线程计算
算法·cuda
2301_788770553 小时前
OJ模拟4
算法
NAGNIP4 小时前
一文搞懂CNN经典架构-AlexNet!
人工智能·算法