⭐️ 什么是数据结构?
百度百科给数据结构的定义:
数据结构(Data Structure)是计算机存储、组织数据的方式。数据结构是指相互之间存在一种或多种特定关系的数据元素的集合。
数据结构就是数据在内存中的存储方式。
⭐️ 什么是算法?
百度百科给算法的定义:
计算机算法是以一步接一步的方式来详细描述计算机如何将输入转化为所要求的输出的过程,或者说,算法是对计算机上执行的计算过程的具体描述。
算法就是将输入数据转换成输出的结果。
注:数据结构和算法是相辅相成的。
💬 算法特点
- 有穷性。 (一个算法应包括有限的操作步骤,而不能是无限的。)
- 确定性。 (算法的每一个步骤应该是确定的,而不是模糊的。)
- 有零个或多个输入。
- 有一个或多个输出。 (没有输出的算法是没有意义的。)
- 有效性。 (算法的每一个步骤应当能有效的执行,并得到确定的结果。)
⭐️ 时间复杂度
百度百科时间复杂度的定义:
在计算机科学中,时间复杂性,又称时间复杂度,算法的时间复杂度是一个函数,它定性描述该算法的运行时间。这是一个代表算法输入值的字符串的长度的函数。时间复杂度常用大O符号表述,不包括这个函数的低阶项和首项系数。使用这种方式时,时间复杂度可被称为是渐近的,亦即考察输入值大小趋近无穷时的情况。
算法中的基本操作的执行次数,为算法的时间复杂度。
💬 大O渐进表示法
- 用常数
1
取代运行时间中所有加法常数。 - 在修改后的运行次数函数中,保留最高次项。
- 如果最高次项系数不是
1
,则去除这个最高次项的系数。
⭕️例1:
// 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;}
}
推导过程:
外层循环执行一次内存循环就要执行 N N N 次,外层循环执行 N N N 次,那么内层循环也要执行 N N N次,所以第一个循环是执行的次数是 N 2 N^2 N2。第二个循环循环 2 N 2N 2N 次。第三个循环循环常数次 10 10 10 次。
执行次数: F ( N ) = N 2 + 2 N + 10 F(N) = N^2 + 2N + 10 F(N)=N2+2N+10
- N = 1000 N = 1000 N=1000 F ( N ) = 1000 ∗ 1000 + 2 ∗ 1000 + 10 = 1002010 F(N) = 1000 * 1000 + 2 * 1000 + 10 = 1002010 F(N)=1000∗1000+2∗1000+10=1002010
实际中计算时间复杂度时,不需要计算精确的执行次数,而只需要大概的执行次数,所以这里用到了大O的渐进表示。
从上面 N = 1000 N = 1000 N=1000 的例子中可以看出来,当 N N N 越来越大的时候,或者 N N N 趋于无穷的时候, 2 N 2N 2N与 10 10 10就起不到作用了,甚至可以忽略不计。
所以 例1
中函数的时间复杂度是 O ( N 2 ) O(N^2) O(N2)。
⭕️例2:
// count的执行次数是多少?
void Func2(int N)
{int count = 0;// 第一个循环for (int k = 0; k < 2 * N ; ++ k){++count;}// 第二个循环int M = 10;while (M--){++count;}
}
推导过程:
第一个循环执行 2 N 2N 2N 次,第二个循环执行常数次 10 10 10 次。
执行次数: F ( N ) = 2 N + 10 F(N) = 2N + 10 F(N)=2N+10。 常数次 10 10 10 在 2 N 2N 2N 面前可以忽略不计,而里面最高次的系数是 2 2 2 也可以忽略不计。
所以 例2
的时间复杂度是 O ( N ) O(N) O(N)。
⭕️例3:
int Func3(int * nums , int numsSize , int target)
{for (int i = 0; i < numsSize; i++) {if(nums[i] == target) {return i;}}return -1;
}
描述代码:Func3
代码是在数组中寻找一个目标值,如果找到目标值,则返回数组当前元素的下标,没有找到返回 -1
。
我们通过 例1
和 例2
发现了大 O O O 渐进表示法去掉了对结果影响不大的项,简洁的表明了执行的次数。
那么有些算法的时间复杂度存在最好、平均、和最坏的情况。
比如 例3
这段代码:
- 最好情况: 1 1 1 次找到
- 最坏情况: N N N 次找到
- 平均情况: N / 2 N/2 N/2 次找到
但是在实际中一般情况下我们只关注算法的最坏运行情况,所以 例3
的时间复杂度为 O ( N ) O(N) O(N)。
⭕️例4:
void Func4(void)
{ int count = 0;for (int i = 0; i < 100; i++) {count++;}
}
例4
中的代码循环是固定的常数次 100 100 100 次,像一般常数级别的次数,时间复杂度用 O ( 1 ) O(1) O(1) 表示。
注:O(1)代表是常数次,并不是一次的意思。
⭐️ 空间复杂度
空间复杂度(Space Complexity)是对一个算法在运行过程中临时占用存储空间大小的量度。空间复杂度算的是变量的个数。也使用大 O O O渐进表示法
像冒泡排序、选择排序…这些算法的空间复杂度是 O ( 1 ) O(1) O(1),因为这些算法只用了常数个变量。而一般的递归算法空间复杂度就是 O ( N ) O(N) O(N)了,每次递归函数都要开辟 N N N个栈帧,每个栈帧使用了常数个空间。
⭕️例5:
void Func5(int nums , int numsSize) {int * temp = (int*)malloc(sizeof(int) * numsSize);int end = numsSize - 1;for (int i = 0; i < numsSize; i++) {temp[i] = nums[end--];}for (int i = 0; i < numsSize; i++) {printf("%d " , temp[i]);}
}
例5
的代码动态的开辟了 numsSize
个空间,所以空间复杂度是 O ( N ) O(N) O(N)。
注:空间复杂度还是要根据实际情况讨论,有些情况空间是会先创建再销毁重复利用空间的。