深入了解,为你解开冒泡排序算法的心结

我们先看一个案例,一组乱序的数据,现在要把它变成从小到大的一组数据

只有一个原则:相邻数据对换位置

看完以后你就知道什么是冒泡算法了

冒泡算法步骤最全案例

 

 
 

再给冒泡算法归纳总结之前,我们先介绍一个什么叫时间复杂度?

时间复杂度是什么?

我们假设计算机运行一行基础代码需要执行一次运算。

int aFunc(void) {
    printf("Hello, World!\n");      //  需要执行 1 次
    return 0;       // 需要执行 1 次
}

那么上面这个方法需要执行 2 次运算

int aFunc(int n) {
    for(int i = 0; i<n; i++) {         // 需要执行 (n + 1) 次
        printf("Hello, World!\n");      // 需要执行 n 次
    }
    return 0;       // 需要执行 1 次
}

这个方法需要 (n + 1 + n + 1) = 2n + 2 次运算。


我们把 算法需要执行的运算次数 用 输入大小n 的函数 表示,即 T(n) 。
此时为了 估算算法需要的运行时间 和 简化算法分析,我们引入时间复杂度的概念。

定义:存在常数 c 和函数 f(N),使得当 N >= c 时 T(N) <= f(N),表示为 T(n) = O(f(n)) 。

根据如下图来了解定义;


 

疑问:T(N)是什么?f(N)又代表什么?

T(n)是算法的执行次数函数

f(N)是时间复杂度函数,一般用O(f(N))表示


我们知道常数项对函数的增长速度影响并不大,所以当 T(n) = c,c 为一个常数的时候,我们说这个算法的时间复杂度为 O(1);如果 T(n) 不等于一个常数项时,直接将常数项省略。

案例一、T(n)=2(常量)  算法的时间复杂度为O(1)

int aFunc(void) {
    printf("Hello, World!\n");      //  需要执行 1 次
    return 0;       // 需要执行 1 次
}

案例二、T(n) =2n + 2,此时时间复杂度为 O(n)

int aFunc(int n) {
    for(int i = 0; i<n; i++) {         // 需要执行 (n + 1) 次
        printf("Hello, World!\n");      // 需要执行 n 次
    }
    return 0;       // 需要执行 1 次
}

高次项对于函数的增长速度的影响是最大的。n^3 的增长速度远超 n^2 ,n^2 的增长速度远超 n ,所以我们直接忽略低此项

比如
T(n) = n^3 + n^2 + 29,此时时间复杂度为 O(n^3)。
比如
T(n) = 3n^3,此时时间复杂度为 O(n^3)。

 综合起来:如果一个算法的执行次数是 T(n),那么只保留最高次项,同时忽略最高项的系数后得到函数 f(n),此时算法的时间复杂度就是 O(f(n))。为了方便描述,下文称此为 大O推导法。

对于循环体的时间复杂度

1、假设循环体的时间复杂度为 O(n),循环次数为 m,则这个循环的时间复杂度为 O(n×m)。

void aFunc(int n) {
    for(int i = 0; i < n; i++) {         // 循环次数为 n+1
        printf("Hello, World!\n");      // 循环体时间复杂度为 O(1)
    }
}

此时时间复杂度为 O((n+1) × 1)=O(n+1),忽略常量,即 O(n)。

2、假设循环体的时间复杂度为 O(n),各个循环的循环次数分别是a, b, c...,则这个循环的时间复杂度为 O(n×a×b×c...)

void aFunc(int n) {
    for(int i = 0; i < n; i++) {         // 循环次数为 n+1
        for(int j = 0; j < n; j++) {       // 循环次数为 n+1
            printf("Hello, World!\n");      // 循环体时间复杂度为 O(1)
        }
    }
}

时间复杂度为 O((n+1) × (n+1) × 1)=O(n^2+2*n+1)=O(n^2)

3、总的时间复杂度等于其中最大的时间复杂度

void aFunc(int n) {
    // 第一部分时间复杂度为 O(n^2)
    for(int i = 0; i < n; i++) {
        for(int j = 0; j < n; j++) {
            printf("Hello, World!\n");
        }
    }
    // 第二部分时间复杂度为 O(n)
    for(int j = 0; j < n; j++) {
        printf("Hello, World!\n");
    }
}

此时间复杂度为 max(O(n^2), O(n)),即 O(n^2)。

 

 

 我们现在回到冒泡排序的话题

若一组数据本身就是从小到大有序的,所需的关键字比较次数C=n-1,

为什么明明是有序的,还要比较n-1次?

我总得知道这组数据是不是有序的吧!

记录移动次数M=0;冒泡排序最好时间复杂度为O(n)


若一组数据从大到小的,需要进行n轮排序,每轮进行n-1次比较的;

且每次比较都必须移动记录三次来达到交换记录位置。在这种情况下,比较和移动次数均达到最大值:

冒泡排序最坏时间复杂度为

综上,因此冒泡排序总的平均时间复杂度为O(n^2)


请问各位,为什么是必须移动记录三次?

发布了1185 篇原创文章 · 获赞 310 · 访问量 117万+

猜你喜欢

转载自blog.csdn.net/qq_37591637/article/details/103594515