数据结构与算法1 -- 数据结构及算法复杂度

前言

最近打算系统性的学习数据结构与算法这一块的内容。在此以前其实对这一块的内容并不陌生,大学中开过这方面的课,可是当时不知道学了有什么用、不知道在哪用、不知道怎么用?也就只是学的勉勉强强,哈哈😄。作了几年开发以后才知道数据结构与算法的重要性,所以打算再系统性的学习(复习)一遍。算法

数据结构

首先借鉴(抄)一段百度百科上对数据结构的定义。数组

数据结构(data structure)是带有结构特性的数据元素的集合,它研究的是数据的逻辑结构和数据的物理结构以及它们之间的相互关系,并对这种结构定义相适应的运算,设计出相应的算法,并确保通过这些运算之后所获得的新结构仍保持原来的结构类型。简而言之,数据结构是相互之间存在一种或多种特定关系的数据元素的集合,即带“结构”的数据元素的集合。“结构”就是指数据元素之间存在的关系,分为逻辑结构和存储结构。数据结构

从定义上能够看到,数据结构研究的是数据的逻辑结构和数据的物理结构以及它们之间的相互关系。那么下面就来看看什么是逻辑结构,什么是物理结构。函数

逻辑结构

数据的逻辑结构,重点在这逻辑二字上。
逻辑结构,顾名思义,数据真正存储的结构可能不是这样的,可是咱们能够认为数据就是这样存储的。这种结构是咱们人为赋予数据的,至于意图,固然就是为了让咱们更加容易理解数据的存储和使用。post

有哪些逻辑结构

  1. 集合结构。集合中任何两个数据元素之间都没有逻辑关系,互不干扰。1对0。
    举例:int a = 2, b = 3;那么a和b之间没有任何关系,它们之间就属于集合结构。
    集合结构
  2. 线性结构。数据元素按照顺序链接成一大串。1对1。
    举例:数组、链表、队列等这样线性排列的数据集合。
    线性结构
  3. 树形结构。树形结构具备分支、层次特性,其形态像现实中的树,所以叫树形结构。1对n,即1对多。
    举例:二叉树、B树、B+树等。
    树形结构
  4. 图形结构。图形结构中的节点按逻辑关系互相缠绕,任何两个节点之间均可以相互链接。m对n,即多对多。
    举例:互联网。
    图形结构

物理结构

物理结构。物理就是指现实中的物体,在这里指的是内存或磁盘。
数据的物理结构表明着数据在内存中的结构。物理结构有两种:学习

  1. 顺序结构。即数据在内存中占用的内存空间是连续的。例如数组int a[10];
    顺序结构
  2. 链式结构。即数据在内存中占用的内存空间是不连续的。例如链表。
    链式结构

算法复杂度

算法复杂度分为时间复杂度和空间复杂度时间复杂度是指执行算法所须要的计算工做量;而空间复杂度是指执行这个算法所须要的内存空间。ui

时间复杂度

算法的时间复杂度是一个函数,它定性描述算法的运行时间。这是一个表明算法输入值的字符串的长度的函数。时间复杂度经常使用大O符号表述,不包括这个函数的低阶项首项系数。使用这种方式时,时间复杂度可被称为是渐近的,即考察输入值大小趋近无穷时的状况。spa

只说概念可能会比较困惑,下面看几个例子就能理解了。设计

  1. 这个算法中,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);
    }
}
复制代码
  1. 这个算法中,由于n是一个常量100,所以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);
    }
}
复制代码
  1. 这个算法中,m和n都是变量 并且在内层for循环 中 执行了两句打印,所以printf语句执行的总次数为m*n*2次。回到上面的概念中看,时间复杂度不包括函数的首项系数。在这里,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");
        }
    }
}
复制代码
  1. 这个算法中,咱们一眼可能看不出x *= 2;一共执行几回,因而咱们只好来进行一些简单的计算了。
    首先假设一共执行了 i 次终于不知足x < n这个条件了,那么咱们就能够获得,当循环结束时,有2*2^i>=n,取临界条件可得2^{i+1}=n,进而可得i = log_2^n - 1。即这个算法的时间复杂度是O(log_2^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)
}
复制代码
  1. 这个算法中,printf("xxxxxxx\n");语句一共执行了2n(n-1)次,即2n^2-2n次,忽略低阶项获得了2n^2,再忽略首项系数最终获得n^2,因此最终这个算法的时间复杂度就表示为O(n^2)
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个例子中2*2^i>=n 忽略大于直接去等了?

借鉴一张图来总结一下常见的时间复杂度及其专业术语吧。

时间复杂度
至于这些阶之间的大小关系,高中数学还没忘的一眼就能看出来吧😂
下面的a表示常数。
O(1)<O(log_a^n)<O(n)<O(nlog_a^n)<O(n^a)<O(a^n)<O(n!)<O(n^n)

空间复杂度

空间复杂度(Space Complexity)是对一个算法在运行过程当中临时占用存储空间大小的量度。

算法占用的空间的计算方法

分析一个算法所占用的存储空间要从各方面综合考虑。如对于递归算法来讲,通常都比较简短,算法自己所占用的存储空间较少,但运行时须要一个附加堆栈,从而占用较多的临时工做单元;若写成非递归算法,通常可能比较长,算法自己占用的存储空间较多,但运行时将可能须要较少的存储单元。

空间复杂度的计算方法

一个算法的空间复杂度只考虑在运行过程当中为局部变量分配的存储空间的大小,它包括为参数表中形参变量分配的存储空间和为在函数体中定义的局部变量分配的存储空间两个部分。

从上面概念能够看出,计算空间复杂度只考虑算法在运行过程当中分配的存储空间的大小,而不考虑算法自己所占的内存空间。

概念看的头疼,仍是来用代码解释怎么计算空间复杂度吧。

  1. 这个算法中,函数的参数有a b c,局部变量有a1 b1 c1,因此总共占用了6块内存空间。但无论几块,是个常数就行,空间复杂度O(1)。
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);
}
复制代码
  1. 这个算法中,每次递归都会开辟一块空间,而且在递归期间这一块空间并不会释放,所以这个算法的空间复杂度是O(n)。
void print2(int n) {
    if (n > 0) {
        int a = n - 1;
        print2(a);
        printf("%d\n", a);
    }
    else {
        printf("%d\n", 0);
    }
}
复制代码

总结

本篇文章主要记录了两大部分

  1. 数据的结构。包括四大逻辑结构两大物理结构
  2. 算法的复杂度。包括时间复杂度空间复杂度及对应的计算方法

本文地址

相关文章
相关标签/搜索