01_时间复杂度与冒泡、插入、选择排序

1. 时间复杂度基础

什么是时间复杂度?

  • 时间复杂度是衡量算法的执行时间随着数据量变化的变化趋势。
  • 也就是一个算法的执行时间,数据量变化时,该算法的执行时间的变化趋势。
  • 用大O表示,计算算法最坏情况下的执行情况

常见的时间复杂度:

  1. 常数时间复杂度:O(1)
    • 算法的执行时间不随数据量变化而变化,也即是说,输入算法的数据怎么变化,算法的执行时间永远是1。
  2. 对数时间复杂度:O(log n)
    • 算法的执行时间随输入数据规模的增加而以对数方式增长,例如二分查找,每一次循环只遍历上一次循环的一半。
  3. 线性时间复杂度:O(n)
    • 算法的执行时间随输入数据规模的增加而线性增长,例如遍历数组。
  4. 线性对数时间复杂度:O(n log n)
    • 算法的执行时间随输入数据规模的增加而以线性对数方式增长,例如快速排序。
  5. 二次时间复杂度:O(n^2)
    • 算法的执行时间随输入数据规模的增加而以平方方式增长,例如冒泡排序。
  6. 指数时间复杂度:O(2^n)
    • 算法的执行时间随输入数据规模的增加而以指数方式增长,例如递归计算斐波那契数列。
  7. 阶乘时间复杂度:O(n!)
    • 算法的执行时间随输入数据规模的增加而以阶乘方式增长,例如全排列。

时间复杂度的计算规则:

  1. 计算算法的执行时间
  2. 简化表达式
    • 只关注最高次项
    • 忽略低次项和常数项

以遍历算法为例计算时间复杂度:

  • 总的执行次数 = 1 +(n + 1) + n + n + 1 = 3n + 3
  • 简化后得到O(n)
    • 忽略低次项和常数项
void printArr(int arr[], int arrLen)                        /* 遍历数组 */
{
    for (int i = 0; i < arrLen; i++)            // int i 执行1次,i < arrLen 执行n+1次,i++ 执行n次
    {
        printf("%d\t", arr[i]);                 // printf 执行n次
    }
    printf("\n");                               // printf 执行1次
}

2. 冒泡、插入、选择排序

2.1 冒泡排序

什么是冒泡排序?

  • 冒泡排序通过重复遍历数组元素,比较相邻两个元素的大小,对满足比较条件的相邻两个元素进行交换,直到遍历两层循环为止。
  • 简单来说,就是每两个元素之间进行比较,例如arr[0] 和 arr[1] 进行比较,如果arr[0] > arr[1],则两个元素交换为止,遍历2次循环,确保每次元素都进行了检查。
void bubbleSort(int arr[], int arrLen)
{
    if (arr <= 0)                               /* 数组长度为0直接返回 */
        return;

    for (int i = 0; i < arrLen; i++)
    {
        for (int j = 0; j < arrLen; j++)
        {
            if (arr[j] > arr[j+1])
            {
                swap(arr, j, j+1);
            }
        }
        printf("外层第%d次遍历:", i+1);
        printArr(arr, arrLen);
    }
}

swap函数:

void swap(int arr[], int a, int b)                         /* 交换函数 */
{
    int item = arr[a];
    arr[a] = arr[b];
    arr[b] = item;
}

printArr函数:

void printArr(int arr[], int arrLen)                        /* 遍历数组 */
{
    for (int i = 0; i < arrLen; i++)            // int i 执行1次,i < arrLen 执行n+1次,i++ 执行n次
    {
        printf("%d\t", arr[i]);                 // printf 执行n次
    }
    printf("\n");                               // printf 执行1次
}

时间复杂度:

  • O(n^2)

执行情况:
![[Pasted image 20250104101353.png]]

2.2 选择排序

什么是选择排序?

  • 选择排序就是在数组范围内进行选择最小的一个原则,把他放在最前边的为止。
  • 比如数组长度n,第一次遍历就是n范围内最小的元素放在0为止上;第二次遍历在1到n的范围内找到最小的,放在1的位置上,以此类推。
void selectSort(int arr[], int arrLen)                     /* 选择排序 */
{
    if (arrLen <= 0)                               /* 数组长度为0直接返回 */
        return;
    
    int minIndex;
    for (int start = 0; start < arrLen; start++)
    {
        minIndex = start;
        for (int end = start; end < arrLen; end++)
        {
            minIndex = arr[end] < arr[minIndex] ? end : minIndex;
        }
        printf("外层第%d次遍历:", start+1);
        printArr(arr, arrLen);
        swap(arr, start, minIndex);
    }
}

时间复杂度:

  • O(n^2)

执行情况:
![[Pasted image 20250104104738.png]]

2.3 插入排序

什么是插入排序?

  • 插入排序是根据每次选择范围的最后一个数与前边一个数进行比骄,如果满足条件,则当前元素的位置与前边元素交换,之后继续将当前元素与前一个元素进行比较,直到第0各位置的元素。
  • 比如
    • 8,3,1,5,2范围,第一次比较范围0到1,把3和8比较,满足元素交换:3,8,1,5,2
    • 3,8,1,5,2范围,第二次比较范围0到2,把1和8进行比较,进行交换,此时3,1,8,5,2;之后1再和3进行比较,一直到0位置,就是1,3,8,5,2。以此类推,一直到遍历完所有元素。
void insertSort(int arr[], int arrLen)                      /* 插入排序 */
{
    int minIndex;
    for (int start = 1; start < arrLen; start++)
    {
        minIndex = start;
        while (minIndex > 0)
        {
            minIndex = arr[minIndex] < arr[minIndex - 1] ? minIndex : minIndex - 1;
            swap(arr, minIndex, minIndex - 1);
        }  
    }
    
}

时间复杂度:

  • 最坏情况:O(n^2)
  • 最好情况:O(n)

执行情况:
![[Pasted image 20250104110211.png]]

3. 结语

时间复杂度是常用的衡量算法效率的指标,用于计算一个算法最坏情况下的执行效率。冒泡排序是无论数据是否有序,时间复杂度都为O(n2),而插入排序的时间复杂度随数据状况而变化,最小为O(n),最大为O(n2)。
本文章是作者对算法的学习与输出巩固,欢迎各位大佬评论与指教,一起进步。

代码文件:

#include <stdio.h>

void swap(int arr[], int a, int b);                         /* 交换函数 */
void printArr(int arr[], int arrLen);                       /* 遍历数组 */
void bubbleSort(int arr[], int arrLen);                     /* 冒泡排序 */
void selectSort(int arr[], int arrLen);                     /* 选择排序 */
void insertSort(int arr[], int arrLen);                     /* 插入排序 */


int main()
{
    int arr[] = {38, 23, 11, 3, 82, 23, 84, 10};
    int arrLen = sizeof(arr) / sizeof(int);

    printf("排序前:\t");
    printArr(arr, arrLen);
    // bubbleSort(arr, arrLen);
    selectSort(arr, arrLen);
    printf("排序后:\t");
    printArr(arr, arrLen);

    return 0;
}

void insertSort(int arr[], int arrLen)                      /* 插入排序 */
{
    int minIndex;
    for (int start = 1; start < arrLen; start++)
    {
        minIndex = start;
        while (minIndex > 0)
        {
            minIndex = arr[minIndex] < arr[minIndex - 1] ? minIndex : minIndex - 1;
            swap(arr, minIndex, minIndex - 1);
        }
        
        
    }
    
}

void selectSort(int arr[], int arrLen)                      /* 选择排序 */
{
    if (arrLen <= 0)                                        /* 数组长度为0直接返回 */
        return;
    
    int minIndex;
    for (int start = 0; start < arrLen; start++)
    {
        minIndex = start;
        for (int end = start; end < arrLen; end++)
        {
            minIndex = arr[end] < arr[minIndex] ? end : minIndex;
        }
        printf("外层第%d次遍历:", start+1);
        printArr(arr, arrLen);
        swap(arr, start, minIndex);
    }
}

void bubbleSort(int arr[], int arrLen)
{
    if (arrLen <= 0)                                        /* 数组长度为0直接返回 */
        return;

    for (int i = 0; i < arrLen; i++)
    {
        for (int j = 0; j < arrLen; j++)
        {
            if (arr[j] > arr[j+1])
            {
                swap(arr, j, j+1);
            }
        }
        printf("外层第%d次遍历:\t", i+1);
        printArr(arr, arrLen);
    }
}

void swap(int arr[], int a, int b)                          /* 交换函数 */
{
    int item = arr[a];
    arr[a] = arr[b];
    arr[b] = item;
}

void printArr(int arr[], int arrLen)                        /* 遍历数组 */
{
    for (int i = 0; i < arrLen; i++)                        // int i 执行1次,i < arrLen 执行n+1次,i++ 执行n次
    {
        printf("%d\t", arr[i]);                             // printf 执行n次
    }
    printf("\n");                                           // printf 执行1次
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值