最近打算系统性的学习数据结构与算法这一块的内容。在此以前其实对这一块的内容并不陌生,大学中开过这方面的课,可是当时不知道学了有什么用、不知道在哪用、不知道怎么用?也就只是学的勉勉强强,哈哈😄。作了几年开发以后才知道数据结构与算法的重要性,所以打算再系统性的学习(复习)一遍。算法
首先借鉴(抄)一段百度百科上对数据结构的定义。数组
数据结构(data structure)是带有结构特性的数据元素的集合,它研究的是数据的逻辑结构和数据的物理结构以及它们之间的相互关系,并对这种结构定义相适应的运算,设计出相应的算法,并确保通过这些运算之后所获得的新结构仍保持原来的结构类型。简而言之,数据结构是相互之间存在一种或多种特定关系的数据元素的集合,即带“结构”的数据元素的集合。“结构”就是指数据元素之间存在的关系,分为逻辑结构和存储结构。数据结构
从定义上能够看到,数据结构研究的是数据的逻辑结构和数据的物理结构以及它们之间的相互关系。那么下面就来看看什么是逻辑结构,什么是物理结构。函数
数据的逻辑结构,重点在这逻辑二字上。
逻辑结构,顾名思义,数据真正存储的结构可能不是这样的,可是咱们能够认为数据就是这样存储的。这种结构是咱们人为赋予数据的,至于意图,固然就是为了让咱们更加容易理解数据的存储和使用。post
物理结构。物理就是指现实中的物体,在这里指的是内存或磁盘。
数据的物理结构表明着数据在内存中的结构。物理结构有两种:学习
int a[10];
算法复杂度分为时间复杂度和空间复杂度
。时间复杂度
是指执行算法所须要的计算工做量;而空间复杂度
是指执行这个算法所须要的内存空间。ui
算法的时间复杂度是一个函数,它定性描述算法的运行时间。这是一个表明
算法输入值
的字符串的长度的函数。时间复杂度经常使用大O符号表述,不包括
这个函数的低阶项
和首项系数
。使用这种方式时,时间复杂度可被称为是渐近的,即考察输入值大小趋近无穷时
的状况。spa
只说概念可能会比较困惑,下面看几个例子就能理解了。设计
printf("------%d-------\n",i);
语句一共会执行n次,且n是输入的变量,所以这个算法的时间复杂度就能够表示为O(n)。void print1(int n) {
// 由于n是变量,因此这个算法的时间复杂度是O(n)
for (int i = 0; i < n; i++) {
printf("------%d-------\n", i);
}
}
复制代码
printf("++++++%d+++++++\n",i);
语句执行的次数就是固定的100次,100是个常数,所以这个算法的时间复杂度被称为是常数阶,用O(1)表示。void print2() {
// 由于n是一个常量100,因此这个算法的时间复杂度是O(1)
int n = 100;
for (int i = 0; i < n; i++) {
printf("++++++%d+++++++\n", i);
}
}
复制代码
printf
语句执行的总次数为不包括
函数的首项系数
。在这里,2就属于首项系数,因此这个算法的时间复杂度就能够表示为O(m*n)。void print3(int m, int n) {
// m和n都是变量,因此时间复杂度是O(m*n)
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
printf("-----------\n");
printf("+++++++++++\n");
}
}
}
复制代码
x *= 2;
一共执行几回,因而咱们只好来进行一些简单的计算了。i
次终于不知足x < n
这个条件了,那么咱们就能够获得,当循环结束时,有低阶项
,因此-1那个常数阶就不要了。void print4(int n) {
/* 设循环i次 则到最后一次 2的(i+1)次方 >= n 因此 i = log2 n - 1 */
int x = 2; // 1次
while (x < n) {
x *= 2; // log以2为底n的对数 - 1次
}
// 因此时间复杂度是 O(log2 n)
}
复制代码
printf("xxxxxxx\n");
语句一共执行了低阶项
获得了首项系数
最终获得void print5(int n) {
for (int i = 0; i < 2n; i++) { // 2n次
for (int j = 0; j < n - 1; j++) { // n - 1次
printf("xxxxxxx\n"); // 2n(n - 1)次
}
}
// 因此时间复杂度是 O(n2)
}
复制代码
经过上面这几个例子,我相信你已经可以理解时间复杂度是个什么东西了。可是仍是须要提醒一句,并非时间复杂度大的算法执行的语句的次数就必定多
,具体执行多少次是要和算法输入n
有关的。而算法的时间复杂度
只是用来估算
当输入的n趋于无穷时
算法执行须要时间的多少
。3d
看到这“估算”二字是否是也就理解了上面例子中为何要忽略低阶项
和首项系数
,以及第4个例子中 忽略大于直接去等了?
再借鉴一张图来总结一下常见的时间复杂度及其专业术语吧。
空间复杂度(Space Complexity)是对一个算法在运行过程当中临时占用存储空间大小的量度。
算法占用的空间的计算方法
分析一个算法所占用的存储空间要从各方面综合考虑。如对于递归算法来讲,通常都比较简短,算法自己所占用的存储空间较少,但运行时须要一个附加堆栈,从而占用较多的临时工做单元;若写成非递归算法,通常可能比较长,算法自己占用的存储空间较多,但运行时将可能须要较少的存储单元。
空间复杂度的计算方法
一个算法的空间复杂度只考虑在运行过程当中为局部变量分配的存储空间的大小,它包括为参数表中形参变量分配的存储空间和为在函数体中定义的局部变量分配的存储空间两个部分。
从上面概念能够看出,计算空间复杂度只考虑算法在运行过程当中分配的存储空间的大小,而不考虑算法自己所占的内存空间。
概念看的头疼,仍是来用代码解释怎么计算空间复杂度吧。
void print1(int a, float b, char c) {
int a1 = a;
float b1 = b;
char c1 = c;
printf("%d\n%f\n%c\n", a1, b1, c1);
}
复制代码
void print2(int n) {
if (n > 0) {
int a = n - 1;
print2(a);
printf("%d\n", a);
}
else {
printf("%d\n", 0);
}
}
复制代码
本篇文章主要记录了两大部分
四大逻辑结构
和两大物理结构
。时间复杂度
和空间复杂度
及对应的计算方法
。