一.什么是时间复杂度
当我第一次看"时间复杂度"这一概念的时候,首先所想到的是代码运行的时间越短,则该代码运行效率越高,但后来仔细一想,如果同样一段代码在不同的电脑上运行,有的电脑配置高,有的电脑配置低,运行的时间自然也是不相同的,这无疑是一种不公平的比较方法。
首先,为什么引进时间复杂度这一概念呢?
引入时间复杂度这一概念的主要原因是为了帮助我们评估和比较不同算法的性能。在解决同一个问题时,可能存在多种不同的算法,它们在执行效率上可能有很大的差异。时间复杂度提供了一种标准化的方法来量化算法的执行时间,从而使我们能够更好地理解算法的性能特征。
时间复杂度是一种函数,定量地描述了该算法运行的时间。既然是一种函数,就涉及到自变量与因变量。因变量代表是时间复杂的规模,自变量是时间复杂度的执行时间。这里的执行时间并不是秒,分钟这类的具体时间 ,它表示的是一种“执行次数”。要想计算时间复杂度首先得找到该算法中的循环,算法中循环执行的次数就是算法的时间复杂度 。
算法的时间复杂度的具体表示为:用大写的 O 来体现算法时间复杂度如O(f(n)),称之为 大 O 记数法。
二.时间复杂度的表示
1.大O的渐进表示法
// 请计算一下Func1中++count语句总共执行了多少次?
void Func1(int N)
{
int count = 0;
for (int i = 0; i < N ; ++ i)
{
for (int j = 0; j < N ; ++ j)
{
++count;
}
}
for (int k = 0; k < 2 * N ; ++ k)
{
++count;
}
int M = 10;
while (M--)
{
++count;
}
printf("%d\n", count);
}
首先,我们有三个循环结构:
- 两个嵌套的
for
循环,每个循环都运行 N 次。 - 一个
for
循环,运行 2 * N 次。 - 一个
while
循环,运行 10 次。
因此,我们可以计算 ++count
语句的总执行次数:
- 在嵌套的
for
循环中,++count
语句会执行 N * N 次。 - 在第二个
for
循环中,执行了 2 * N 次++count
。 - 在
while
循环中,执行了 10 次++count
。
2.时间复杂度的分类
// 计算Func2的时间复杂度?
void Func2(int N)
{
int count = 0;
for (int k = 0; k < 2 * N ; ++ k)
{
++count;
}
int M = 10;
while (M--)
{
++count;
}
printf("%d\n", count);
}
Func2:
这个函数包含一个 for
循环和一个 while
循环,它们的迭代次数分别为 2 * N 和 10。因此,时间复杂度为 O(N)。
// 计算Func3的时间复杂度?
void Func3(int N, int M)
{
int count = 0;
for (int k = 0; k < M; ++ k)
{
++count;
}
for (int k = 0; k < N ; ++ k)
{
++count;
}
printf("%d\n", count);
}
Func3:
这个函数包含两个 for
循环,它们的迭代次数分别为 M 和 N。因此,时间复杂度为 O(M + N)。
// 计算Func4的时间复杂度?
void Func4(int N)
{
int count = 0;
for (int k = 0; k < 100; ++ k)
{
++count;
}
printf("%d\n", count);
}
Func4:
这个函数只包含一个固定次数的 for
循环,其迭代次数是一个常数 100,不受输入参数 N 的影响。因此,时间复杂度为 O(1)。
三.计算冒泡排序的时间复杂度
#include <stdio.h>
// 冒泡排序函数
void bubbleSort(int arr[], int n) {
int i, j;
for (i = 0; i < n-1; i++) {
// 每一轮把最大的元素移到最后
for (j = 0; j < n-i-1; j++) {
if (arr[j] > arr[j+1]) {
// 交换 arr[j] 和 arr[j+1]
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
int main() {
int arr[] = {3, 1, 5, 6, 7,2,4,8};
int n = sizeof(arr) / sizeof(arr[0]);
printf("Original array: ");
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
bubbleSort(arr, n);
printf("Sorted array: ");
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
冒泡排序是一种简单直观的排序算法,它重复地遍历要排序的列表,比较相邻的元素并交换它们,如果顺序不正确就交换。重复这个过程直到不需要交换,即列表已经排序完成。现在我们来分析冒泡排序的时间复杂度。
假设列表长度为 N。
冒泡排序的基本思想是每次从头开始比较相邻的两个元素,如果逆序就交换它们,直到将最大的元素移到列表的末尾。重复这个过程,每次都将当前未排序部分的最大元素交换到未排序部分的最后。所以,冒泡排序的核心是一次遍历会将一个最大(或最小)的元素放到正确的位置上。
最坏情况下,需要进行 N-1 轮比较。在每一轮比较中,需要比较的次数都会减少,因为每一轮都会有一个最大(或最小)的元素被放到了正确的位置上。因此,在第一轮中需要比较 N-1 次,第二轮中需要比较 N-2 次,以此类推,直到最后一轮只需要比较 1 次。
所以,总的比较次数可以表示为:
下面是一个示意图,说明了冒泡排序的过程:
四.什么是空间复杂度
void BubblesSort(int[] array) {
boolean flag=true;//此处创建了一个标志性变量flag
for(int i=0;i<array.length-1;i++) {//创建临时变量i
for(int j=0;j<array.length-1-i;j++) {//创建临时变量j
if(array[j]>array[j+1]) {
flag=false;
swap(array[j],array[j+1]);
}
}
if(flag==true)
break;
}
}
可以看到,上面的代码创建了flag,i,j三个临时变量,用大O表示空间复杂度是O( 1 )
需要说明的是,此处的要进行排序的数组array并能计算在空间复杂度里,因为它不是BubbleSort方法临时创建的变量