前言: PASCAL之父,瑞士著名计算机科学家沃思(Niklaus Wirth)教授曾提出:算法+数据结构=程序。算法是科学的解决问题的思路,是对业务的实现思路,而数据结构是对数据存储组织的抽象(数据之间的关系以及操作)。使用计算机解决问题的过程如下图。其中提取操作对象与找出操作对象之间的关系便是数据结构部分,设计算法便是算法部分。
数据结构与算法笔记分为三大部分,第一部分是绪论部分主要讲一些关于数据结构与算法的基本概念。第二部分讲基本的数据结构,分为线性结构和非线性结构两块。第三部分讲基本的数据处理技术,像查找、排序之类的。
1.数据结构
1.1数据的相关概念
数据:比如身份信息、工作信息的集合,关键在于描述客观事物并且可以输入到计算机中被程序处理。
数据对象:是数据的子集,是具有相同性质的数据元素的集合,比如身份信息。
数据元素:是数据的基本单位,简称元素或者结点、顶点等。比如每一个人的身份信息。是数据的个体。
数据项:构成数据元素的不可分割的最小单位,比如身份信息中的姓名。
数据类型:是值集合+操作。
包括原子类型(值的集合+操作,比如int、char)、结构类型(结构的集合+操作,比如list、map)、抽象数据类型(数据对象+数据关系+操作,比如抽象人、车)
1.2数据结构的相关概念
结构:数据不是孤立的,他们存在某种关系,这种关系的集合称为结构,比如前后关系等等
数据结构:相互之间存在一种或者多种特定关系的数据元素的集合。也就是说描述存在某些关系的数据元素的集合。关键在于数据元素。
1.3数据结构的三要素
数据结构的三要素:逻辑结构、物理结构、数据的运算
1.3.1逻辑结构
逻辑结构:是对数据元素之间关系的描述,与数据的存储结构无关,是从具体问题中抽象出来的数学模型。分为两大类线性结构和非线性结构。其中非线性结构分为集合、树形结构、图状结构三类。
线性结构:比如,买东西时排的队,分数由大到小等
集合:除了所有元素都在一个集合之类无其他关系,比如整数集合等
树形结构:一对多的关系,比如狗的种类
图状结构:多对多的关系,比如城市的交通网络
1.3.2物理结构
存储结构:也叫物理结构,是数据元素的逻辑结构在计算机中的表示(映像)。分为顺序存储、链式存储、索引存储、散列存储四类(前两者时重点)
顺序存储:逻辑上相邻的数据元素在物理位置上也相邻。知道一个元素的位置经过简单运算便可知道其他元素的位置
链式存储:逻辑上相邻的数据元素在物理位置上不确定。通过指针表示相邻元素的位置。只能实现顺序存储不可实现随机存储。
索引存储:不仅仅要存储信息外还要建立索引表。索引表中有多个索引项。索引项包含关键字和地址。通过关键字找到地址,通过地址找到所要元素。(了解)
散列存储:根据关键字的函数运算找到存储地址。(了解)
逻辑结构是数据结构的抽象,物理结构是数据结构的实现,两者综合起来建立了数据元素的结构关系。
1.3.3数据运算
数据的运算:包括运算的定义和实现,运算的定义针对逻辑结构,运算的实现针对存储结构。
eg:逻辑结构为ACB,存储结构为addr0 A addr1 B addr 2 C,即看逻辑结构我们要求顺序为ACB,所以运算的定义最后要满足的是ACB这个顺序,但存储中却是ABC,所以在运算的实现时我们要根据存储结构去实现顺序为ACB。
2算法
2.1算法基本概念
算法:对特定问题求解步骤的一种描述,它是指令的有限序列,其中的每条指令表示一个或者多个操作。
算法特性:有穷性、确定性、可行性、输入、输出
程序:某种程序设计语言对算法的具体实现
算法与程序的关区别:算法是解决问题的一种方法的描述。程序则是对算法的实现
有穷性:算法必须又穷, 程序可以无穷
正确性:算法必须是正确的,程序可以是错误的
描述方法:算法可以用伪代码、程序语言等描述,程序只能用程序语言编写并可以运行
算法设计目标:正确性、可读性、健壮性、效率与存储量
3算法效率的度量
3.1有关算法效率的基本概念
语句频度:该语句可能重复执行的次数
T(n):所有语句的频度之和,n为问题的规模。
eg:
int sum=0;
for(int i = 0; i < n; i++)
{
sum+=i;
}
此时T(n)=1+n.
3.2时间复杂度
时间复杂度 :T(n)=O(f(n)),其中O表示T(n)与f(n)在n趋于正无穷时为同阶无穷大。(如上个例子,则时间复杂度为O(n))
时间复杂度分为最坏时间复杂度、最好时间复杂度、平均时间复杂度,一般用最坏,有时用平均。
时间复杂度运算规则:
加法规则:T(n)=T1(n)+T2(n)=O(f(n))+O(g(n))=O(max(f(n),g(n))) (相加取最大的时间复杂度,两个独立的循环)
乘法规则:T(n)=T1(n)*T2(n)=O(f(n))*O(g(n))=O(f(n)*g(n)) (相乘取对应同阶无穷大的乘积,嵌套循环)
通常采用基本运算频度来分析算法时间复杂度(个人理解是找最深层循环内的语句即执行次数最多的语句)
常见时间复杂度:O(1)<O(log2n)<O(n)<O(nlog2n)<O(n2)<O(n3)<O(2n)<O(n!)<O(nn)
小结:
1>是什么?
时间复杂度是指执行这个算法所需要的计算工作量;而空间复杂度是指执行这个算法所需要的内存空间。
2>怎么计算?
找最深层循环内的语句即执行次数最多的语句,然后计算其执行次数(使用求和等,找出语句频度函数f(n)),最后找其等价无穷小(数量级)。
注意:找出语句频度函数f(n)与n之间的关系最重要!!!
3>示例:
实例1
for (int i = 1; i <= n; i++)
{
for (int j = 1; j < i; j++)
{
for (int k = 1; k < j; k++)
{
x += 1;
}
}
}
所以时间复杂度为O(n * n * n)。
实例2
int i=1;
while (i<=n)
i=i*2;
执行1次 i=2
执行2次 i=2 * 2
执行3次 i=2 * 2 * 2
……
执行f(n)次 i=2的f(n)次方
那么需要满足条件:2的(f(n)-1)次方<=n<2的f(n)次方
两边取对数可得:log2为底n<f(n)<=log2为底n对数+1
最后对f(n)分析数量级可得log2为底n的对数
3.3空间复杂度(了解)
空间复杂度:算法消耗的存储空间,记s(n)=o(g(n))。
除本身使用的指令、常数、变量和输入数据外,还需要一些对数据进行操作的工作单元和存储为实现算法所需的一些信息的辅助空间。
算法原地工作指算法所需辅助空间为常量,O(1)
例如,把一个数组的元素逆序。
算法1:首尾交换,要1个中间变量temp,空间复杂度为:O(1)
算法2:新建1个数组,先逆序存放到新数组,在把新数组的元素存放到原数组中,空间复杂度为O(n)。