数据结构--Chapter1(绪论)

来源:互联网 发布:91.v6p.co index.php 编辑:程序博客网 时间:2024/06/03 14:29

1.1基本概念和术语

1.1.1数据与数据结构

表1.1 生产订单表订单号开工时间制单000012015-11-17张三000022015-11-18李四000032015-11-19王五

     数据(Data),数据就是所有能有效地输入到计算机中并且能够被计算机处理的符号总称,也是计算机程序处理对象的集合,是计算机程序加工的“原料”。例如,一个利用数值分析方法求解代数方程的程序,其处理对象是整数和实数等数值数据;一个编译程序或文字处理程序的处理对象是字符串。数据还包括图像、声音等非数值数据。

    数据元素(Data Element),数据元素是数据中的一个“个体”,是数据的基本组织单位。在计算机程序中通常将它作为一个整体进行考虑和处理。在不同条件下,数据元素又可称为节点、顶点和记录。上表中的一行数据称为一个数据元素或者记录。

    数据项(Data Item),数据项是数据元素的组成部分,是具有独立含义的标识单位。一个数据元素可以由若干个数据项组成。数据项又可分为两种,一种是简单数据项,另一种是组合数据项。例如“订单号”和“制单”都是简单数据项,因为它们在数据处理时不能再分割;而“开工时间”则是一个组合数据项,它可以更进一步分为“年”、“月”、“日”等更小的数据项。

    数据对象(Data Object),数据对象是性质相同的元素的集合。例如,在对生产订单进行查询时,计算机所处理的数据对象是上表中的所有数据,这张表就可以看成是一个数据对象。

    数据结构(Data Structure),数据结构是互相之间存在一种或多种特定关系的数据元素的集合。在不同的地方,数据结构有不同的定义,但是无论是何种定义,实际上都离不开对数据的逻辑结构、数据的存储结构和数据的操作的3个方面的考虑。

1.逻辑结构

    数据的逻辑结构是指各个数据之间的逻辑关系,是呈现在用户面前的、能感知到的数据元素的组织形式。表1.1中第一个数据元素是生产订单号为00001的订单记录,这条记录称之为开始节点;最后一个生产订单号为00003的订单记录,这条记录称之为终端节点,其他数据元素的前、后都有且仅有一个数据元素与它相邻,分别称之为前驱和后继。

    按照数据元素之间逻辑关系的特性来分,可将数据结构分为以下4类:

1)集合

    集合中数据元素除了“同属于一个集合”的特性外,数据元素之间无其他关系,它们之间的关系称为是松散性的。

2)线性结构

     线性结构中数据元素之间存在“一对一”的关系。即若结构非空,则它有且仅有一个开始节点和终端节点,开始节点没有前驱但是有一个后继,终端节点没有后继但是有一个前驱,其余节点有且仅有一个前驱和后继。

3)树形结构

    树形结构中数据元素之间存在“一对多”的关系。即若结构非空,则它有一个称为根的节点,此节点没有前驱结点,其余节点有且仅有一个前驱,所有节点都可以有多个后继。

4)图形结构

    树形结构中数据元素之间存在“多对多”的关系。即若结构非空,则在这种数据结构中任何节点都可能有多个前驱和后继。

    有时也将逻辑结构分为两大类,一类是线性结构,另一类非线性结构。其中集合、树和图都属于非线性结构。

    数据的逻辑结构的表述涉及两个方面的内容,一方面是数据元素,另一方面是数据元素之间的关系。所以从形式上可以采用一个二元组来定义,定义形式为:Data_Structures=(D,R)

其中,D是数据元素的有限集,R是D上关系的有限集。设R1∈R,则R1是一个DXD的关系子集,若a1,a2∈D,<a1,a2>∈R1,则称a1是a2的前驱,a2是a1的后继,对R1而言,a1和a2是相邻的节点。

2.存储结构

    数据的存储结构(物理结构)是数据的逻辑结构在计算机中的实现。它包括数据元素值在计算机中的存储表示和逻辑关系在计算机中的存储表示这两部分,是依赖于计算机的。数据元素值在数据域中是以二进制的存储形式表示的,而数据元素之间逻辑关系的存储表示则通常有以下4种表示方式。

1)顺序存储方式

    顺序存储方式是指将所有的数据元素存放在一片连续的存储空间中,并使逻辑上相邻的数据元素其对应的物理位置也相邻,即数据元素的逻辑位置关系与物理位置关系保持一致。顺序存储结构通常借助程序设计语言中的数组加以实现。

2)链式存储方式

    连式存储方式不要求将逻辑上相邻的数据元素存储在物理上相邻的位置,即数据元素可以存储在任意的物理位置上。没一个数据元素所对应的存储表示由两部分组成,一部分存放数据元素本身,另一部分用于存放表示逻辑关系的指针,即数据元素之间的逻辑关系是由附加指针来表示的。

3)索引存储方式

    索引存储方式在存储数据元素的同时,还增设了一个索引表。索引表中的每一项包括关键字和地址,其中关键字是能够唯一标识一个数据元素的数据项,地址是指示数据元素的存储地址或 存储区域的首地址。

4)散列存储方式

    散列存储(也称哈希存储)方式是指将数据元素存储在一片连续的区域内,每一个数据元素的具体地址是根据该数据元素的关键字值,通过散列(哈希)函数直接计算出来的。

    以上四种存储方式中,顺序存储和链式存储是两种最基本、最常用的存储方式。索引存储和散列存储是两种为了提高查找效率而经常采用的存储方式。

    任何一种数据存储方式都有各自的优缺点。在实际应用中,选择何种存储方式来表示数据的逻辑结构,要分别根据各种存储结构的特点和处理问题时需进行的一些操作视具体情况而定,总体原则就是要达到操作方便、高效。

3.数据的操作

    数据的操作就是对数据进行某种方法的处理,也称数据的运算。只有当数据对象按一定的逻辑结构组织起来,并选择了适当的存储方式存储到计算机中时,与其相关的运算才有了实现的基础,所以,数据的操作也可被认为是定义在数据逻辑结构上的草,单操作的实现却要考虑数据的存储结构。

1.1.2  抽象数据类型

    抽象(Abstract)就是抽取反映问题本质的东西,忽略其非本质的细节。也就是在问题求解过程中只要求人们关注“做什么”,而不是“则么做“的过程。这种把数据的使用与实现分离开来的做法称为数据抽象(Data Abstraction)。

    数据的抽象是通过抽象数据类型来实现的。抽象数据类型(Abstract Data Type,ADT)是指一数据值的集合和定义在这个集合上的一组操作。它不包括数据的计算机存储表示,而且这里的操作时脱离了具体实现的抽象操作,即不涉及他的实现细节。换句话说,抽象数据类型是指隐藏了数据的存储结构并且不涉及操作的实现细节的数据类型。

    在Java语言中,抽象数据类型的买描述可采用两种方法:第一种使用抽象类(abstract class)表示,抽象类型的实现用继承该抽象类的子类表示;第二种使用Java接口(interface)表示,抽象类型的实现用实现该结构的类表示。下面通过一个具体实例来说明抽象数据类型的描述和实现。

例1.1 用Java来描述复数的抽象数据类型。假设复数的操作只包含:构造一个实部和虚部为给定值的复数,读取和修改复数的实部和虚部以及两个复数的求和。

package adt;//复数抽象数据类型的接口表示public interface IComplex {public double getReal();// 取实部public void setReal(double real);// 修改实部public double getImag();// 取虚部public void setImag(double imag);// 修改虚部public void add(IComplex Z);// 两个复数的求和}

例1.2 编写实现例1.2中复数抽象数据类型的Java类代码。

package complex;import adt.IComplex;public class Complex implements IComplex {private double real;private double imag;// 构造一个实数public Complex(double real, double imag) {this.real = real;this.imag = imag;}@Override// 取实部public double getReal() {return real;}@Override// 修改实部public void setReal(double real) {this.real = real;}@Override// 取虚部public double getImag() {return imag;}@Override// 修改虚部public void setImag(double imag) {this.imag = imag;}@Override// 两个复数的求和public void add(IComplex Z) {if (Z != null) {real += Z.getReal();imag += Z.getImag();}}}

1.2 算法和算法分析

1.2.1 算法的基本概念

    算法是对特定问题求解步骤的一种描述。它是指令的有限序列,其中每条指令表示一个或多个操作。算法一般应具有以下五种性质。

1)有穷性:无论在何种情况下,一个算法都必须在执行有限步骤之后结束,而且每一步骤都是在又穷的时间内完成。

2)确定性:可以从两方面来理解算法的确定性,一方面是指算法中每一条指令的确定性,即每一条指令都有确切的含义,不会产生二义性;另一方面是指算法输出结果的确定性,即在任何条件下,只要是同样的一组输入就能得到相同的输出结果。

3)有效性:算法的有效性是指算法中每一条指令的有效性,即算法中每一条指令的描述都是符合语法规则、满足语意要求的,都能够被人或机器确切执行,并能通过已经实现的基本运算执行有限次来完成。

4)输入:一个算法具有零个或多个输入,这些输入是某个算法的已实现的初始条件,它取自于某个特定对象的集合,是待处理的信息。

5)输出:一个算法必须有一个或多个输出,这些输出是与输入有着某种特定关系的量,它是经处理后的信息。

    可以这样理解:程序=算法+数据结构

1.2.2 算法的描述

    算法的描述可以采用某种语言,也可以借助数据流程图来表示。描述算法的语言主要有3种形式:自然语言、程序设计语言和伪代码。这里采用Java程序设计语言描述算法。

    下面通过一个例子了解用Java程序设计语言描述算法的基本方法。

例1.3 给出求整型数组a中最大值的算法

public class test {static int maxEle(int[] a) {int n = a.length;int max = a[0];for (int i = 1; i < n; i++) {if (max < a[i]) {max = a[i];}}return max;}}

    该算法首先将数组中第0个数据元素是为最大者,并将它保存在变量max中;然后从第一个数据元素开始将数组中它后面的所有数据元素依次与max进行值得比较,  若遇到比max值更大的数据元素,则将此数据元素值存入max中。当后面的n-1个数据元素都比较完毕后,保存在max中的值就是数组a中的最大值。

1.2.3 算法分析

    算法分析的任务就是利用某种方法,对每一个算法讨论起各种复杂度,以此来评判某个算法适用于哪一类问题,活着那一类问题宜采用某个算法。算法的复杂度是衡量算法优劣的重要依据。算法的复杂度通常体现在时间复杂度和空间复杂度两个指标上。

1.算法的时间复杂度分析

    算法时间复杂度的高低直接反应算法执行时间的长短,而算法的执行时间需要通过依据该算法编写的程序在计算机上执行所消耗的时间来度量。

    对于一个特定的算法,其执行时间只依赖于问题的规模,即可看作是问题规模的一个函数,什么函数则要视具体算法而定。所将某一问题规模为n的算法所需的执行时间的度量记为T(n),并将T(n)称为算法的时间复杂度。下面各出时间复杂度的大O表示法。

    大O表示法:称一个函数g(n)是O(f(n)),当且仅当存在常数c>0和n0>=1,对一切n>n0均有|g(n)|&lt;=c|f(n)|成立,也称函数g(n)以f(n)为界或者称g(n)囿于f(n)。记作g(n)=O(f(n))。 定义:如果一个问题的规模是n,解这一问题的某一算法所需要的时间为T(n),它是n的某一函数。T(n)称为这一算法的“时间复杂度”。

    假设一个算法是由n条指令序列所构成的集合,则:

    算法的执行时间=操作1的执行时间 + 操作2的执行时间 + ... + 操作n的执行时间

    操作i的执行时间=操作i的执行次数*操作i执行一次的时间

    算法的执行时间与操作i的执行次数之和成正比。所以可以通过计算依据算法编制的程序中每条语句的语句频度之和的值来评估一个算法的执行时间。所谓语句频度就是语句重复执行的次数。

例1.4 求下面算法的时间复杂度

public static int sum(int a[]) {int n = a.length, s = 0;// 1for (int i = 0; i < n; i++) {// n+1s += a[i];// n}return s;// 1}

    此算法中所有语句的执行次数之和为2n+3,则T(n)=O(n)。

    用渐进分析法对算法的执行时间进行评估,是根据算法执行过程中需要实施的关键操作的数目的多少来衡量的。所谓关键操作就是算法中最主要的操作。用这种方法对算法的时间复杂度进行分析时,只需正确选择一个算法中的关键操作并通过计算关键操作的语句频度来估算出算法的执行时间。例如,例1.4中的算法中的第四行语句“s+=a[i];”是该算法执行的关键操作,它的语句频度是n,所以T(n)=O(n)。

例1.5 求下面算法的时间复杂度

public static void myOut(int n) {for (int i = 1; i <= n; i = 2 * i) {System.out.println(i);}}

    该算法执行的关键操作时第3行语句,假设它的语句频度是f(n),则有2^f(n)<=n,即f(n)<=log(2)(n)。根据时间复杂度的定义,可得到该算法的时间复杂度为T(n)=O(log(2)(n))。

2.算法按时间复杂度分类

    算法可按其执行时间分为两类:多项式时间算法和指数时间算法。

    多项式时间算法安递增关系如下:

    常量阶:O(1)、对数阶:O(log(2)(n))线性阶:O(n)、线性对数阶:O(nlog(2)(n))、平方阶:O(n^2)、立方阶:O(n^3)

    指数时间算法的时间复杂度形式为O(a^n),按递增关系如下:

    O(2^n)<O(n!)<O(n^n)。

3.算法的空间复杂度分析

    空间复杂度(Space Complexity)是对一个算法在运行过程中临时占用存储空间大小的量度,记做S(n)=O(f(n))。比如直接插入排序的时间复杂度是O(n^2),空间复杂度是O(1) 。而一般的递归算法的空间复杂度是O(n)。


0 0
原创粉丝点击