01.01.01 数据结构与算法(第 01 天)


算法 + 数据结构 = 程序
简单来说,「算法」就是解决问题的方法或者过程。如果我们把问题看成是函数,那么算法就是将输入转换为输出的过程。「数据结构」是数据的计算机表示和相应的一组操作。「程序」则是算法和数据结构的具体实现。
对于待解决的问题,我们追求的是:选择更加合适的「数据结构」,使用花费时间更少、占用空间更小的「算法」。

1. 数据结构

数据结构(Data Structure):带有结构特性的数据元素的集合。
可以按照数据的 「逻辑结构」 和 「物理结构」 来进行分类。

1.1 数据的逻辑结构

逻辑结构(Logical Structure):数据元素之间的相互关系。

  1. 集合结构
    集合结构:数据元素同属于一个集合,除此之外无其他关系。
    合结构中的数据元素是无序的,并且每个数据元素都是唯一的,集合中没有相同的数据元素。集合结构很像数学意义上的「集合」。
    在这里插入图片描述
  2. 线性结构
    线性结构:数据元素之间是「一对一」关系。
    线性结构中的数据元素(除了第一个和最后一个元素),左侧和右侧分别只有一个数据与其相邻。线性结构类型包括:数组、链表,以及由它们衍生出来的栈、队列、哈希表。
    在这里插入图片描述
  3. 树形结构
    树形结构:数据元素之间是「一对多」的层次关系。

最简单的树形结构是二叉树。这种结构可以简单的表示为:根, 左子树, 右子树。 左子树和右子树又有自己的子树。当然除了二叉树,树形结构类型还包括:多叉树、字典树等。
在这里插入图片描述
4. 图形结构
图形结构:数据元素之间是「多对多」的关系。
图形结构是一种比树形结构更复杂的非线性结构,用于表示物件与物件之间的关系。一张图由一些小圆点(称为 「顶点」 或 「结点」)和连结这些圆点的直线或曲线(称为 「边」)组成。
在图形结构中,任意两个结点之间都可能相关,即结点之间的邻接关系可以是任意的。图形结构类型包括:无向图、有向图、连通图等。
在这里插入图片描述

1.2 数据的物理结构

物理结构(Physical Structure):数据的逻辑结构在计算机中的存储方式。

计算机内有多种存储结构,采用最多的是这两种结构:「顺序存储结构」、「链式存储结构」。

  1. 顺序存储结构
    顺序存储结构(Sequential Storage Structure):将数据元素存放在一片地址连续的存储单元里,数据元素之间的逻辑关系通过数据元素的存储地址来直接反映。
    在这里插入图片描述

在顺序存储结构中,逻辑上相邻的数据元素在物理地址上也必然相邻
这种结构的优点是:简单、易理解,且实际占用最少的存储空间。
缺点是:需要占用一片地址连续的存储单元;并且存储分配要事先进行;另外对于一些操作的时间效率较低(移动、删除元素等操作)。

  1. 链式存储结构
    链式存储结构(Linked Storage Structure):将数据元素存放在任意的存储单元里,存储单元可以连续,也可以不连续。
    在这里插入图片描述

链式存储结构中,逻辑上相邻的数据元素在物理地址上可能相邻,可也能不相邻。其在物理地址上的表现是随机的。链式存储结构中,一般将每个数据元素占用的若干单元的组合称为一个链结点。每个链结点不仅要存放一个数据元素的数据信息,还要存放一个指出这个数据元素在逻辑关系的直接后继元素所在链结点的地址,该地址被称为指针。换句话说,数据元素之间的逻辑关系是通过指针来间接反映的

这种结构的优点是:存储空间不必事先分配,在需要存储空间的时候可以临时申请,不会造成空间的浪费;一些操作的时间效率远比顺序存储结构高(插入、移动、删除元素)。
缺点是:不仅数据元素本身的数据信息要占用存储空间,指针也需要占用存储空间,链式存储结构比顺序存储结构的空间开销大。

2. 算法

算法(Algorithm):解决特定问题求解步骤的准确而完整的描述,在计算机中表现为一系列指令的集合,算法代表着用系统的方法描述解决问题的策略机制
简单而言,「算法」 指的就是解决问题的方法
展开来讲,算法是某一系列运算步骤,它表达解决某一类计算问题的一般方法,对这类方法的任何一个输入,它可以按步骤一步一步计算,最终产生一个输出。它不依赖于任何一种语言,可以用 自然语言、编程语言(Python、C、C++、Java 等)描述,也可以用 伪代码、流程图 来表示。

2.1 算法的基本特性

算法其实就是一系列的运算步骤,这些运算步骤可以解决特定的问题。除此之外,算法 应必须具备以下特性:

输入:对于待解决的问题,都要以某种方式交给对应的算法。在算法开始之前最初赋给算法的参数称为输入。比如示例
中的输入就是出发地和目的地的参数(北京,上海),示例
中的输入就是
个整数构成的数组。一个算法可以有多个输入,也可以没有输入。比如示例
是对固定问题的求解,就可以看做没有输入。
输出:算法是为了解决问题存在的,最终总需要返回一个结果。所以至少需要一个或多个参数作为算法的输出。比如示例
中的输出就是最终选择的交通方式,示例
中的输出就是和的结果。示例
中的输出就是排好序的数组。
有穷性:算法必须在有限的步骤内结束,并且应该在一个可接受的时间内完成
确定性:组成算法的每一条指令必须有着清晰明确的含义,不能令读者在理解时产生二义性或者多义性。就是说,算法的每一个步骤都必须准确定义而无歧义。
可行性:算法的每一步操作必须具有可执行性,在当前环境条件下可以通过有限次运算实现。也就是说,每一步都能通过执行有限次数完成,并且可以转换为程序在计算机上运行并得到正确的结果。

2.2 算法追求的目标

所需运行时间更少(时间复杂度更低)
占用内存空间更小(空间复杂度更低)
除了对运行时间和占用内存空间的追求外,一个好的算法还应该追求以下目标:
正确性:正确性是指算法能够满足具体问题的需求,程序运行正常,无语法错误,能够通过典型的软件测试,达到预期的需求。
可读性:可读性指的是算法遵循标识符命名规则,简洁易懂,注释语句恰当,方便自己和他人阅读,便于后期修改和调试。
健壮性:健壮性指的是算法对非法数据以及操作有较好的反应和处理。

3. 算法复杂度简介

算法复杂度(Algorithm complexity):在问题的输入规模为 n的条件下,程序的时间使用情况和空间使用情况。

3.1 时间复杂度简介

时间复杂度(Time Complexity):在问题的输入规模为n的条件下,算法运行所需要花费的时间,可以记作为 T(n)。

我们将 基本操作次数 作为时间复杂度的度量标准。换句话说,时间复杂度跟算法中基本操作次数的数量正相关。
基本操作 :算法执行中的每一条语句。每一次基本操作都可在常数时间内完成。
基本操作是一个运行时间不依赖于操作数的操作。

3.2 时间复杂度计算

时间复杂度的函数可以表示为:T()=O(f(n))。它表示的是随着问题规模 n 的增大,算法执行时间的增长趋势跟 f(n) 相同。
O 是一种渐进符号,称作算法的 渐进时间复杂度(Asymptotic Time Complexity),简称为 时间复杂度。
求解时间复杂度一般分为以下几个步骤:

  • 找出算法中的基本操作(基本语句):算法中执行次数最多的语句就是基本语句,通常是最内层循环的循环体部分。
  • 计算基本语句执行次数的数量级:只需要计算基本语句执行次数的数量级,即保证函数中的最高次幂正确即可。像最高次幂的系数和低次幂可以忽略。
  • 用大 O 表示法表示时间复杂度:将上一步中计算的数量级放入 O 渐进上界符号中。

常数O(1)
一般情况下,只要算法中不存在循环语句、递归语句,其时间复杂度都为 O(1)。
线性O(n)
一般含有非嵌套循环,且单层循环下的语句执行次数为 n 的算法涉及线性时间复杂度。这类算法随着问题规模 n 的增大,对应计算次数呈线性增长。
平方O( n 2 n^2 n2)
一般含有双层嵌套,且每层循环下的语句执行次数为 n 的算法涉及平方时间复杂度。这类算法随着问题规模 n 的增大,对应计算次数呈平方关系增长。
阶乘O(n!)
阶乘时间复杂度一般出现在与「全排列」、「旅行商问题暴力解法」相关的算法中。这类算法随着问题规模 n 的增大,对应计算次数呈阶乘关系增长。
对数O(logn)
对数时间复杂度一般出现在「二分查找」、「分治」这种一分为二的算法中。这类算法随着问题规模 n 的增大,对应的计算次数呈对数关系增长。
线性对数O(nlogn)
线性对数一般出现在排序算法中,例如「快速排序」、「归并排序」、「堆排序」等。这类算法随着问题规模 n 的增大,对应的计算次数呈线性对数关系增长。
根据从小到大排序,常见的时间复杂度主要有:
O(1) < O(logn) < O(n) < O(nlogn) < O( n 2 n^2 n2) < O( n 3 n^3 n3) < O( 2 n 2^n 2n) < O(n!) < O( n n n^n nn)

3.3 最佳、最坏、平均时间复杂度

最佳时间复杂度:每个输入规模下用时最短的输入所对应的时间复杂度。
最坏时间复杂度:每个输入规模下用时最长的输入所对应的时间复杂度。
平均时间复杂度:每个输入规模下所有可能的输入所对应的平均用时复杂度(随机输入下期望用时的复杂度)。

4. 空间复杂度

4.1 空间复杂度简介

空间复杂度(Space Complexity):在问题的输入规模为 n 的条件下,算法所占用的空间大小,可以记作为 S(n)。一般将 算法的辅助空间 作为衡量空间复杂度的标准。
除了执行时间的长短,算法所需储存空间的多少也是衡量性能的一个重要方面。空间复杂度的函数可以表示为 S(n)=O(f(n)),它表示的是随着问题规模 n 的增大,算法所占空间的增长趋势跟 f(n) 相同。
算法的空间复杂度更容易计算,主要包括「局部变量(算法范围内定义的变量)所占用的存储空间」和「系统为实现递归(如果算法是递归的话)所使用的堆栈空间」两个部分。

4.2 空间复杂度计算

常数O(1)
线性O(n)

def algorithm(n):
    if n <= 0:
        return 1
    return n * algorithm(n - 1)

上述代码采用了递归调用的方式。每次递归调用都占用了 1 个栈帧空间,总共调用了 n 次,所以该算法的空间复杂度为 O(n)。
对数O(logn)
平方O( n 2 n^2 n2)

  • 27
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值