0基础学习数据结构(0绪论)


前言:由于课程安排,得用搁了一年没用过的C语言开始完成数据结构作业~希望将自己摸索路上的思考记载下来并分享给大家,也希望各位大佬能批评指正,后续所有代码也会传到GitHub上

绪论

  • 数据结构的基本概念,研究对象
  • 抽象数据型(ADT)
  • 算法复杂性
  • 逐步求精方法

简要介绍历史:
数据结构创始人——Donald.E.Knuth
31岁开始出版历史性经典巨著:The Art of Computer Programming
计划共写7卷,然而出版三卷之后便已震惊世界,获得图灵奖(36岁)

简要介绍数据结构重点:
如果将学习程序设计语言比作识字的过程,那么学习数据结构则是相当于写作文的过程,再进一步的算法设计与分析就相当于写文章了。

1.1数据结构的兴起和发展

数据结构随着程序设计的发展而发展

  • 无结构阶段:在简单数据上作复杂计算
  • 结构化结构:数据结构+算法=程序
  • 面向对象阶段:(对象+行为)= 程序
  • ……

1.2研究对象与基本概念

数据结构有如下基本概念:

  • 数据项:构成数据元素的不可分割的最小单位
  • 数据对象:具有相同性质的数据元素的集合
  • 结点:数据元素在计算机内的威串表示
  • 域(字段):数据元素中数据项在计算机内的表示
姓名语文数学
张三8554
李四9287

数据元素:单个学生的成绩(表中的一行)
数据项:某门成绩(表中的一格)

数据结构:根据各种不同的数据集合和数据元素之间的关系,研究如何表示、存储和操作(查找、插入、删除、修改、排序)这些数据
通常可以用一个二元组<D,R>来表示或写成DS=<D,R>
其中D是数据集合(数据对象),R是D中数据元素之间所存在的关系的有限集合

数据结构按照视角的不同,可以分为逻辑结构存储结构
数据的逻辑结构是从具体问题抽象出来的人为定义的数据模型
数据的存储结构是计算机分配内存,存储结点和结点关系

逻辑结构包含如下四类(以后重点学习的方向):

  • 集合::数据元素之间就是“属于同一个集合”
  • 数据线性结构:数据元素之间存在着一对一的线性关系
  • 树型结构:数据元素之间存在着一对多的层次关系
  • 图型结构:数据元素之间存在着多对多的任意关系

存储结构包含如下两种基本存储结构

  • 顺序存储结构(逻辑关系用存储位置(例如数组连续存储))
  • 链接存储结构(逻辑关系用指针)

简单而言,数据的逻辑结构属于用户视图,存储结构属于计算机视图

学习的五个方面
数据的逻辑结构
数据的存储结构
数据的算法及实现
操作算法的性能
实际应用
线性结构
线性表
队列
……
非线性结构
集合结构
树型结构
图型结构
顺序存储
链式存储
索引存储
散列存储
检索
排序
插入
删除
……
时间和空间效率,时间和空间复杂度

1.3抽象数据类型

抽象数据类型(A bstract D ata T ype)
定义:一个数学模型和在该模型上定义的操作集合的总称

  • ADT是程序设计语言中数据类型概念的进一步推广和进一步抽象(不指定具体编程语言,不指定参数具体类型,一切都抽象)
  • 同一数学模型上定义不同的操作集,则表示代表不同的ADT
  • 例如: A D T   i n t = ( { x ∣ x ∈ Z } , { + , − , ∗ , / , % , ⩽ , = = } ) ADT\,int =(\{x|x \in Z\},\{+,-,*,/,\%,\leqslant,==\}) ADTint=({xxZ},{+,,,/,%,,==})

区别数据类型、数据结构和ADT

不同含义:

  • 数据类型是一组值的集合
  • 数据结构则是数据元素之间的抽象关系
  • 抽象数据型是一个数学模型及在该模型上定义的操作集的总称

相互关系:

  • 数据结构是抽象数据型(ADT)中数学模型的表示
  • ADT是数据类型的进一步推广和进一步抽象

1.4算法及算法分析

算法(Algorithm):是对特定问题 求解步骤的一种描述,是指令的有限序列
算法的五大特性:

  • 输入:一个算法有零个或多个输入
  • 输出:一个算法有一个或多个输出
  • 有穷性:一个算法必须总是在执行有穷步之后结束,且每一步都在有穷时间内完成
  • 确定性:算法中的每一条指令必须有确切的含义,对于相同的输入只能得到相同的输出
  • 可行性:算法描述的操作可以通过已经实现的基本操作执行有限次来实现

算法分析::对算法所需计算机资源—时间和空间进行估算

  • 时间复杂性(Time Complexity)
  • 空间复杂性(Space Complexity)
    算法分析——时间复杂度分析
=
算法的执行时间
每条语句执行次数之和
基本语句的执行次数
每条语句执行时间之和
执行次数*执行一次的时间
单位时间
指令系统,编译的代码质量

算法的执行时间是基本(操作)语言重复操作执行的次数,它是问题规模的一个函数。将这个函数的渐近阶称为该算法的时间复杂度

for (int i=1;i<=n;i++)
{
	for(int j=1;j<=n;j++)
	{
		x++;
	}
}
  • 问题规模(输入量的多少):n
  • 基本语句(执行次数与整个算法的执行时间 成正比的操作):x++
  • 时间复杂度: O ( n 2 ) O(n^2) O(n2)

算法分析----大O记号

定义:设T(n)和f(n)是定义在正整数集合上的两个函数,若存在两个正的常数 c c c n 0 n_0 n0,对于任意 n ≥ n 0 n≥n_0 nn0(当问题规模充分大时在渐近意义下的阶),都有 T ( n ) ≤ c × f ( n ) T(n)≤c×f(n) T(n)c×f(n),则称 T ( n ) = O ( f ( n ) ) T(n)=O(f(n)) T(n)=O(f(n))

定理:若 A ( n ) = a m n m + a m − 1 n m − 1 + … … + a 1 n + a 0 A(n)=a_mn^m+a_{m-1}n^{m-1}+……+a_1n+a_0 A(n)=amnm+am1nm1++a1n+a0是一个m次多项式,则 A ( n ) = O ( n m ) A(n)=O(n^m) A(n)=O(nm)
说明:在计算算法时间复杂度时,可以忽略所有低次幂(低阶)项和最高次幂(最高阶)项的系数

for (i=1; i<=n; ++i)
	for (j=1; j<=n; ++j)
	{
		c[i][j]=0;
		for (k=1; k<=n; ++k)
			c[i][j]+=a[i][k]*b[k][j];
	}
  • 问题规模:n
  • 基本语句:*
  • 复杂度: O ( n 3 ) O(n^3) O(n3)

算法分析----时间复杂性分析的基本方法

时间复杂性的运算法则

T 1 ( n ) = O ( f ( n ) ) , T 2 ( n ) = O ( g ( n ) ) T_1(n)=O(f(n)),T_2(n)=O(g(n)) T1(n)=O(f(n))T2(n)=O(g(n)),则
①加法规则: T 1 ( n ) + T 2 ( n ) = O ( m a x { f ( n ) , g ( n ) } ) T_1(n)+T_2(n)= O(max\{f(n),g(n)\}) T1(n)+T2(n)=O(max{f(n),g(n)})
②乘法规则: T 1 ( n ) ∗ T 2 ( n ) = O ( f ( n ) ⋅ g ( n ) ) T_1(n)*T_2(n) = O(f(n)·g(n)) T1(n)T2(n)=O(f(n)g(n))

时间复杂性的分析方法
  • 求程序中各语句、各模块的运行时间
  • 求整个程序的运行时间。
  • 各种语句和模块分析应遵循的规则是:
    (1)赋值语句或读/写语句:
    运行时间通常取O(1) .有函数调用的除外,此时要考虑函数的执行时间
    (2)语句序列:
    运行时间由加法规则确定,即该序列中耗时最多的语句的运行时间
    (3)分支语句:
    运行时间由条件测试(通常为O(1) )加上分支中运行时间最长的语句的运行时间
    (4)循环语句:
    运行时间是对输入数据重复执行n次循环体所耗时间的总和
    每次重复所耗时间包括两部分:一是循环体本身的运行时间;二是计算循环参数、测试循环终止条件和跳回循环头所耗时间。后一部分通常为O(1)
    通常,将常数因子忽略不计,可以认为上述时间是循环重复次数n和m的乘积,其中m是n次执行循环体当中时间消耗最多 的那一次的运行时间(乘法规则)
    当遇到多重循环时,要由内层循环向外层逐层分析。因此,当分析外层循环的运行时间时,内层循环的运行时间应该是已知的。此时,可以把内层循环看成是外层循环的循环体的一部分
    (5)函数调用语句:
    ①若程序中只有非递归调用,则从没有函数调用的被调函数开始,计算所有这种函数的运行时间。然后考虑有函数调用的任意一个函数P,在P调用的全部函数的运行时间都计算完之后,即可开始计算P的运行时间
    ②若程序中有递归调用,则令每个递归函数对应于一个未知的时间开销函数T(n),其中n是该函数参数的大小,之后列出关于T的递归方程并求解

例:求解n!递归算法的时间复杂度

long fact(int n)
{
    if ((n == 0) || (n == 1))
        return (1);
    else
        return (n * fact(n – 1));
}

时间复杂度的递归方程
T ( n ) = { C 当n=0,n=1时 G + T ( n − 1 ) 当n>1时 T(n)=\begin{cases} C& \text{当n=0,n=1时} \\ G+T(n-1)& \text{当n>1时} \end{cases} T(n)={CG+T(n1)n=0,n=1n>1
解递归方程:
T ( n ) = G + T ( n − 1 ) T ( n − 1 ) = G + T ( n − 2 ) … … T ( 2 ) = G + T ( 1 ) T ( 1 ) = C → T ( n ) = G ∗ ( n − 1 ) + C 取 f ( n ) = n   ∴ T ( n ) = O ( f ( n ) ) = O ( n ) \begin{aligned} T(n) &=G+T(n-1)\\ T(n-1) &=G+T(n-2)\\ &……\\ T(2) &=G+T(1)\\ T(1) &=C\\ \rightarrow T(n) = G*(n-1)+C\\ 取f(n)=n\ \therefore T(n)=O(f(n))=O(n) \end{aligned} T(n)T(n1)T(2)T(1)T(n)=G(n1)+Cf(n)=n T(n)=O(f(n))=O(n)=G+T(n1)=G+T(n2)=G+T(1)=C

空间资源开销

  • 对于空间开销,也可以实行类似的渐进分析方法
  • 很多算法使用的数据结构是静态的存储结构,即存储空间在算法执行过程中并不发生变化
  • 使用动态数据结构算法的存储空间是变化的,在算法运行过程中有时会有数量级的增大或缩小。对于这种情况,空间开销的分析和估计是十分必要的

时空资源折中原理

同一个问题求解,一般会存在多种算法,这些算法在时空开销上的优劣往往表现出“时空折中”(trade-off)的性质

  • 为了改善一个算法的时间开销,往往以增大空间开销为代价
  • 有时,为了缩小算法的空间开销,也可以牺牲计算机的运行时间,通过增大时间开销来换取存储空间的节省

1.5 逐步求精的程序设计方法(Cont.)

  1. 模型化(建模)
  2. 确定算法
  3. 逐步求精(根据程序中使用的数据形式,定义若干ADT)
  4. ADT的实现(选择适当的数据结构表示数学模型,并用相应的函数实现每个操作)

例:交叉路口的交通安全管理问题

  • 问题描述
    一个具有多条通路的交叉路口,当允许某些通路上的车辆在交叉路口“拐弯”时,必须对其他一些通路上的车辆加以限制,不允许同时在交叉路口“拐弯”,以免发生碰撞。所有这些可能的“拐弯”组成一个集合
  • 基本要求
    这个集合分成尽可能少的组,使得每组中所有的“拐弯”都能同时进行而不发生碰撞。这样,每组对应一个指挥灯,因而实现了用尽可能少的指挥灯完成交叉路口的管理。

根据上述步骤

  1. 模型化:
    转换成“着色问题”
    • 用图作为交叉路口的数学模型;
    • 每个“拐弯”对应图中的一个顶点;
    • 若两个“拐弯”不能同时进行,则用一条边把这两个“拐弯”所对应的两个结点连接起来,并且说这两个顶点是相邻的
  2. 确定算法:
    可以考虑“贪心”算法和“穷举”算法
    “贪心”算法的思想:
    • 首先用第一种颜色对图中尽可能多的
      顶点着色(尽可能多表现出“贪心”)
    • 然后用第二种颜色对余下的顶点中尽
      可能多的顶点着色
    • 如此直至所有顶点都着色完成
  3. 逐步求精:
    考虑用伪代码定义出ADT,再对ADT实现进行优化
  4. 对ADT中的抽象数据型结合题目进行具体定义
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值