刚接触数据结构(Date Structure)的童鞋可能会存在各种各样的疑问。
什么是数据结构?
什么是算法?
同一个问题有不同的算法,到底哪一种好?
希望我的这篇Blog可以帮大家解决一点心中的疑惑,下面我们来一起看看数据结构、算法、时间复杂度和空间复杂度吧!
文章目录
什么是数据结构?
Data Structure 是计算机存储、组织数据的方式,指相互之间存在一种或者多种特定关系的数据元素的集合
什么是算法?
Algorithm是为了解决一个特定问题产生的一系列过程与方法。
简单了解了数据结构和算法,我们知道根据同一个问题可设计出来多种算法,到底哪一种算法比较好呢?面对这样的疑问,我们下面来引入今天的重头戏——时间复杂度和空间复杂度
时间复杂度(Time Complexity)
时间复杂度是一个衡量算法好坏的标准(刻度尺)
注意:不可以用算法运行的绝对时间来直接衡量算法的优劣(时间受到外部环境影响太大)
那我们应该怎么衡量呢?
引入一个小概念:CPU主频:每秒运行指令的个数
这样比较算法运行的指令(基本指令)个数就能很好的解决衡量算法快慢的问题
F(n):基本指令执行的次数,n:数据规模
实际中我们计算时间复杂度时,并不一定要计算精确的执行次数,而只需要大概执行次数,那么我们使用渐进表示法 O(Big O notation):用来描述函数渐进行为的数学符号。
大O渐近表示法化简F(n):保留最高次项,最高次项系数化为1(忽略影响最小的部分)
举个例子:
F(n)= n^2+2×n+10
O(F(n))= O(n^2) 时间复杂度衡量随着数据规模变化,程序运行时间的变化趋势。
时间复杂度分为:最好 平均 最坏(一般情况下参考最坏时间复杂度)
可以形象的理解为计算时间复杂度为悲观算法
让我们来看几个例子
例:计算冒泡排序的时间复杂度
图解:
具体实现:
void Swap(int *n,int *m)
{
int temp = *n;
*n = *m;
*m = temp;
}
void BubbleSort(int arr[], int sz)
{
//一共经过n次冒泡过程,一次冒泡过程,可以把最大的数放到最后
for (int i = 0; i < sz-1; i++)
{
//假设有序
int flag = 1;
//两两比较
for (int j = 0; j < sz - 1 - i; j++)
{
if (arr[i]>arr[i + 1])
{
Swap(&arr[i],&arr[i+1]);
flag = 0;
}
}
//一趟冒泡结束仍有序则数组已经顺序
if (flag == 1)
{
break;
}
}
}
冒泡排序实际是一个减治算法:经过一个固定的步骤之后,数据的规模减1,并且可以按照同样的方式对余下的部分进行处理。
根据时间复杂的概念:基本指令执行(n-1+n-2+……+1)次,相当于等差数列求和 ,(a1+an)×n/2 结果:O(n^2)
例:计算二分查找的时间复杂度
图解:
具体实现:
int binary_search(int arr[], int k, int sz)
{
//确定被查找范围的左右下标
int left = 0;//左下标
int right = sz - 1;//右下标
while (left <= right)
{
//一次二分查找
int mid = (left + right) / 2;//中间元素下标
if (arr[mid] > k)//与目标数进行比较重新确定范围
{
right = mid - 1;
}
else if (arr[mid] < k)
{
left = mid + 1;
}
else
{
return mid;
}
}
//找不到
return -1;
}
二分查找实际是一种分治算法:将规模大小为N的算法分为K个规模较小的子问题,这些子问题相互独立且性质与原问题相同,再去求子问题的解。
看基本指令的执行次数:x = log n 所以时间复杂度为:O(log (n))
例:计算斐波那契数列递归的时间复杂度
图解:
具体实现:
int fib(int n)
{
if (n < 2)
{
return n;
}
else
{
return fib(n - 1) + fib(n - 2);
}
}
此题基本指令执行次数相当于等比数列求和:a1(1-q^n)/1-q = 2^n-1 即:O(2^n)
空间复杂度(Space Compexity)
空间复杂度是对一个算法在运行过程中临时占用存储空间的大小的量度,空间复杂度不是程序占用了多少bytes的空间,而是计算变量的个数,也用大O渐近法表示。(输入中使用的空间不算)
int * a = (int*)malloc(n) //堆上开辟和n相关的空间
int a[n];//栈上开启空间
递归函数 //调用栈占用空间和n的关系
同样我们来看几个例子
例:计算冒泡排序的空间复杂度
图解和具体实现见上文冒泡排序算法中,数组长度 使用了常数个额外空间,所以空间复杂度为 O(1)
例:计算斐波那契数列的空间复杂度
图解和具体实现见上文斐波那契数列算法中:动态开辟了 n个空间,所以空间复杂度为O(n)
了解了这些相信大家也对我们开篇提出的问题有了一定的认识,在数据结构方面知识的学习中,我们应当多画图、多思考、去理解代码本身的含义,这样才能对数据结构相关知识有一个全方位的认知!