1. 时间复杂度基础
什么是时间复杂度?
- 时间复杂度是衡量算法的执行时间随着数据量变化的变化趋势。
- 也就是一个算法的执行时间,数据量变化时,该算法的执行时间的变化趋势。
- 用大O表示,计算算法最坏情况下的执行情况
常见的时间复杂度:
- 常数时间复杂度:O(1)
- 算法的执行时间不随数据量变化而变化,也即是说,输入算法的数据怎么变化,算法的执行时间永远是1。
- 对数时间复杂度:O(log n)
- 算法的执行时间随输入数据规模的增加而以对数方式增长,例如二分查找,每一次循环只遍历上一次循环的一半。
- 线性时间复杂度:O(n)
- 算法的执行时间随输入数据规模的增加而线性增长,例如遍历数组。
- 线性对数时间复杂度:O(n log n)
- 算法的执行时间随输入数据规模的增加而以线性对数方式增长,例如快速排序。
- 二次时间复杂度:O(n^2)
- 算法的执行时间随输入数据规模的增加而以平方方式增长,例如冒泡排序。
- 指数时间复杂度:O(2^n)
- 算法的执行时间随输入数据规模的增加而以指数方式增长,例如递归计算斐波那契数列。
- 阶乘时间复杂度:O(n!)
- 算法的执行时间随输入数据规模的增加而以阶乘方式增长,例如全排列。
时间复杂度的计算规则:
- 计算算法的执行时间
- 简化表达式
- 只关注最高次项
- 忽略低次项和常数项
以遍历算法为例计算时间复杂度:
- 总的执行次数 = 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)
执行情况:
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)
执行情况:
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)
执行情况:
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次
}