1.1数据结构的研究内容
例一:学生学籍管理系统
学号 | 姓名 | 性别 | 籍贯 | 专业 |
---|---|---|---|---|
60214201 | A | 男 | 安徽 | 计算机科学技术 |
60214202 | B | 男 | 福建 | 计算机科学技术 |
60214203 | C | 女 | 吉林 | 计算机科学技术 |
60214204 | D | 女 | 山东 | 计算机科学技术 |
操作对象:每位学生信息
操作算法:查询、插入、修改、删除等
操作对象之间的关系:线性关系
数据结构:线性数据结构、线性表
例二:人机对弈问题(井字棋 )
计算机操作对象:各种棋局状态,即描述棋局的格局信息
计算机的算法:走棋,及选择一种策略使棋局状态发生变化
操作对象之间关系:非线性关系、树(类似于文件系统的系统结构图)
例三:地图导航——求最短路径(最快路径)
网状结构 图论
操作对象:各地点及路的信息
计算机算法:设计信号灯,求出各个可同时通行的路的集合
对象之间的关系:非线性关系,网状结构
“非数值计算”的程序设计问题的数学模型,是诸如表、树和图之类的具有逻辑关系的数据
数据结构是研究非数值计算的程序设计中计算机的操作对象以及他们之间的关系和操作的学科
1.2基本概念术语
数据
能输入计算机且能被计算机处理的各种符号的集合
分为数值型数据和非数值型数据
数值型数据:整数、实数等
非数值型数据:文字、图像、图形、声音等
数据元素(元素、记录、结点、顶点):是数据基本单位,在计算机中是整体考虑
一个数据元素可由很多数据项组成
数据项是最小单位
数据对象:性质相同的数据元素的集合,是数据的一个子集。
数据结构
数据元素的相互关系是结构
数据结构是带结构的数据元素的集合
1.数据元素之间的逻辑关系,也称为逻辑结构
2.数据元素及其关系在计算机内存中的表示(又称为映像),称为数据的物理结构或数据的存储结构
3.数据的运算和实现,即对数据元素可以施加的操作以及这些操作在相应的存储结构上的实现。
分为逻辑结构和物理结构
逻辑结构:是具体问题抽象的数学模型,与数据存储无关,独立于计算机,描述逻辑关系
物理结构(存储结构):数据元素及其关系在计算机存储器中的结构(存储方式),是数据结构在计算机中的表示。
逻辑结构是数据结构的抽象,存储结构是数据结构的实现
逻辑结构的种类
一、线性和非线性
线性结构:
仅有一个开始和终端的结点,并且所有结构只有一个直接前趋和一个直接后继。
如:线性表、栈、队列、串
非线性结构:
一个节点多直接前趋和直接后继
如:树、图
二、集合、线性、树、图四种结构
集合结构:结构中的数据元素除了同属于一个集合的关系外i,无任何其他关系
线性结构:数据元素一对一的线性关系
树形结构:数据元素一对多的层次关系
图状结构或网状结构:结构中的数据元素多对多。
存储结构
不同的存储结构有很多不同的算法
一、顺序存储结构
用计算机中一组连续的数据单元依次存储(有先后顺序)数据元素,数据元素之间的逻辑关系由元素的存储位置来表示
在C语言中一般使用数组的方式。
二、链接存储结构
用一组任意的存储单元存储数据元素,数据元素之间的逻辑元素用指针表示
用一个数据元素,通过指针,找到另一个数据元素,形成链式结构
在C语言中用指针实现链式存储结构。
三、索引存储结构
在建立存储结点信息的同时,还建立了附加的索引表。
索引表中的每一项成为一个索引项
索引项的一般形式:(关键字、地址)
关键字是唯一能标识一个结点的那些数据项
若每个节点在索引表中都有一个索引项,则该索引项称为稠密索引;若一组节点对应一个索引项,则称为稀疏索引
四、散列存储结构
在查找中会详细介绍。
1.3数据类型和抽象数据类型的表示与实现
在高级程序设计语言中必须对每个程序中出现的每个变量常量或表达式,明确说明他们所属的数据类型。
例如C语言中:基本数据类型、构造数据类型、指针、空类型、或者用typedef自己定义数据类型
一些基本数据结构直接用数据类型实现,如数组、字符串等等
而栈、队列、树、图等,不能直接用数据类型来表示
高级语言中的数据类型规定了程序执行期间变量和表达的所有可能取值范围。
抽象数据类型(ADT)
指数学模型以及定义在此数据模型上的一组操作
抽象数据类型不考虑计算机内的具体存储结构与运算的具体实现算法
抽象数据类型可用(D,S,P)三元组表示
其中:D是数据对象。
S是D上的关系集。
P是对D的基本操作集。
抽象数据类型的定义
例一:计算复数的程序
#include<stdio.h> typedef struct { float realpart; //定义实部 float imagpart; //定义虚部 }Complex; //typedef函数用于定义一个用户自己的数据类型 //形式一为:typedef 用户自己数据类型所需要的数据类型 用户自己数据类型的名称 //例如:typedef int inty 定义一种名称为inty的int类型变量 // typedef float inth 定义一种名称为inth的int类型变量 //形式二为:typedef struct{ // 结构体中内容 //} 该结构体用户需要的名称 //例如:typedef struct { // int year; // int month; // int day; //} Date;定义一种名称为Date的含有三种int型变量的结构体类型的数据类型 //在struct的数据类型中,我们可以把其名字置于struct之后,结构内容之前 //构造复数 Complex assign(float real,float imag) { Complex c; c.realpart = real; c.imagpart = imag; return c; } //在结构体变量中,c.realpart表示访问结构体类型变量C中的realpart部分。 //两复数求和 Complex add(Complex A,Complex B){ Complex c; c.realpart = A.realpart + B.realpart; c.imagpart = A.imagpart + B.imagpart; return c; } //两复数求差 Complex minus(Complex A, Complex B) { Complex c; c.realpart = A.realpart - B.realpart; c.imagpart = A.imagpart - B.imagpart; return c; } int main() { Complex b,c,d,e; b = assign(2.0, 4.0); c = assign(1.0, 1.0); d = add(b, c); e = minus(b, c); printf("c=%f+%f i\n", c.realpart, c.imagpart); printf("b=%f+%f i\n", b.realpart, b.imagpart); printf("d=%f+%f i\n", d.realpart, d.imagpart); printf("d=%f+%f i\n", e.realpart, e.imagpart); return 0; }
例二:录入输出学生信息的函数
#include <stdio.h> #include <string.h> // 定义抽象数据类型 Student typedef struct { char name[50]; int age; float gpa; } Student; // 函数用于初始化学生信息 void initializeStudent(Student *student, const char *name, int age, float gpa) { strcpy(student->name, name); student->age = age; student->gpa = gpa; } //当传递字符串时,使用指向常量的指针可以避免在内存中复制整个字符串。如果直接传递字符串作为参数,则需要将整个字符串复制到函数的栈帧中,这会占用额外的内存。使用指针可以传递任意长度的字符串,而不受到参数长度的限制。这意味着可以以相对较小的开销传递大型字符串 //strcpy函数用于字符串的复制 //如strcpy(student->name,name)表示将student结构体数据类型中的name部分的字符串复制给name中 //->用于结构体中目标内容的读取与写入 //如student->age表示读取结构体类型数据中的age部分的内容进行写入 //注意,箭头的左侧必须是一个指向结构体或联合体的指针。这是因为箭头运算符用于访问通过指针引用的结构体或联合体的成员变量。 //=这个运算符是将右侧的值给左侧 // 函数用于打印学生信息 void printStudentInfo(const Student *student) { printf("Name: %s\n", student->name); printf("Age: %d\n", student->age); printf("GPA: %.2f\n", student->gpa); } //上述函数printStudentInfo的定义参数中定义了一个Student类型的名为student的指针变量 //这个名为student的Student类型的指针变量实质上是一个能够放下一组Student数据的地址 //因为const的缘故,在 printStudentInfo 函数内部,尝试对 student 指针所指向的 Student 类型数据进行修改操作(比如修改姓名、年龄、或GPA等),将会导致编译错误。 int main() { // 创建两个学生对象并初始化 Student student1, student2; initializeStudent(&student1, "Alice", 20, 3.5); initializeStudent(&student2, "Bob", 21, 3.2); // 打印学生信息 printf("Student 1:\n"); printStudentInfo(&student1); printf("\nStudent 2:\n"); printStudentInfo(&student2); return 0; }
C语言并没有直接支持ADT的语法结构。
1.4算法和算法实例
算法的定义:对特定问题求解方法和步骤的描述,指令的有限序列。其中每个指令代表一个或多个操作
算法的描述:
1.自然语言:英语、中文
2.流程图:传统流程图、NS流程图
3.伪代码、类语言
4.程序代码
算法的特性
1.有穷性:有穷步后结束
2.确定性:每一条指令有确切定义,没有二义性对于相同输入只有一种输出
3.可行性
4.输入:零个或多个输入
5.输出:一个或多个输出
算法设计要求
1.正确
2.可读
3.健壮
输入非法数据时,算法要做出相应反应或处理,不能输出莫名其妙的结果。处理出错,不应是中断程序执行,而应是返回一个表示错误或错误性质的值
4.高效
少时间,少存储
算法的分析
时间效率的度量
即在计算机上执行时的消耗时间。
事后统计:算法实现,测算其时间和空间开销
事前分析:消耗资源的估算=简单操作次数x一个简单操作时间(与硬件有关)
仅比较次数的数量级
若有某个辅助函数f(n),当n趋近于无穷大时,T(n)/f(n)的极限值是不为0的常数,则称f(n)是T(n)的同数量级函数。记作 T(n)=O(f(n)),O(f(n))称为算法渐进时间复杂度(O是数量级的符号),简称时间复杂度
将代码中执行次数与f(n)相同的代码操作记作基本操作,一般情况下,我们只考虑算法的基本操作
基本方法:
1.找出执行次数最多的语句为基本语句
2.计算基本语句的频度得到问题规模n的某函数
3.去掉低次项和高次项的系数得数量级
执行次数随输入改变:考虑最坏、最好、平均
O()的加法原则:取最大数量级,乘法直接相乘
空间效率的度量
本身需要占据空间、辅助空间(辅助变量所需的数量n)
O(n)