数据结构学习笔记系列1
视频网址: https://www.bilibili.com/video/av18586085
汇总贴链接:https://blog.youkuaiyun.com/DX5618258/article/details/104085229
1.1.1关于数据组织
例1.如何在书架上摆放图书
操作1:新书如何插入
操作2:如何查找书
方法1:随便放
操作1:哪里有空放哪里
操作2:累死
方法2:按照书名的拼音字母顺序排放
操作2:二分查找(如:从L开始分前后区(类似解方程的二分法))
操作1:新书插入难(其后的每本书都需要后移)
方法3:把书架划分几块区域,每块区域指定摆放某种类别的书,在每种类别内按照书名的拼音字母顺序排放
操作1:新书插入(先确定类别,二分查找,移除空位)
操作2:限定类别,再二分查找
问题:1.空间如何分配 2.类别分多细
总结:解决问题方法的效率和数据的组织方式有关
1.1.2关于空间使用
例2:写程序实现printN,使得传入一个正整数N的参数后,能顺序打印从1到N的全部正整数。
以下为两种输出程序: 1. 循环输出 2. 递归输出
#include <iostream>
void PrintN(int N);
//程序入口
int main()
{
int N;
scanf_s("%d" , &N );
PrintN( N );
return 0;
}
//循环输出
void PrintN(int N)
{
for (int i = 1; i <= N;i++)
{
printf("%d\n",i);
}
}
//递归输出(占用大量内存后再输出)
void PrintN(int N)
{
if (N)
{
PrintN(N-1);
printf("%d\n", N);
}
return;
}
问题:当N达到10000量级时(每台机器的配置和性能可能有所区别,当N足够大时结果都是一样的),递归输出程序出现异常,原因是占用内存过大,程序崩溃
总结:解决问题方法的效率和空间的利用效率有关
注:scanf、gets等语句无法正常使用,按照错误列表的提示修改指定位置的scanf和gets等语句,scanf改为scanf_s,gets改为gets_s……(而且,scanf在读取数据时不检查边界,可能会造成内存访问越界的问题,使用scanf_s,会多一个参数来控制读取的字符数量,这样确实比使用scanf输入更加安全。)
1.1.3 关于算法效率
例3:写程序计算给定多项式在给定点x处的值
f(x)=a_0+a_1 x+⋯+a_(n-1) x^(n-1)+a_n x^n
double f1(int n, double a[], double x)
{
double p = a[0];
for (int i = 1; i <= n;i++) { p += (a[i]*pow(x,i)); }
return p;
}
f(x)=a_0+x(a_1+x(⋯(a_(n-1)+x(a_n ))⋯))
double f2(int n, double a[], double x)
{
double p = a[n];
for (int i = n; i > 0;i--) { p = a[i - 1] + x * p; }
return p;
}
例3的问题具体化:多项式简化为系数以所在项索引值相同的多项式,即,
并计算该多项式在点x=1.1处的值f(1.1)。
#include <iostream>
#include <time.h> //clock ()函数需要调用此头文件
#include <math.h> //在vs中可以不调用此头文件也可以计算pow ()函数
//clock_t是clock()函数返回的变量类型,两个变量代表程序运行起止时间
clock_t start, stop;
//记录被测函数的运行时间,单位是秒
double duration;
//定义多项式最大项数
#define MAXN 10
//定义函数执行次数
#define MAXK 1e7
//测试函数的声明
double f1(int n, double a[], double x);
double f2(int n, double a[], double x);
int main()
{
/*声明并初始化系数数组*/
double a[MAXN];
for (int i = 0; i < MAXN; i++)
{
a[i] = (double)i;
}
start = clock();/*开始计时*/
for (int i = 0; i < MAXK; i++)
{
f1(MAXN - 1, a, 1.1);
}
stop = clock(); /*结束计时*/
duration = (((double)(stop - start)) / CLK_TCK) /MAXK;
printf("ticks1 = %f\n",(double)(stop - start));
printf("duration1 = %6.2e\n",duration);
start = clock();/*开始计时*/
for (int i = 0; i < MAXK; i++)
{
f2(MAXN - 1, a, 1.1);
}
stop = clock();/*结束计时*/
duration = (((double)(stop - start)) / CLK_TCK) / MAXK;
printf("ticks2 = %f\n", (double)(stop - start));
printf("duration2 = %6.2e\n", duration);
return 0;
}
double f1(int n, double a[], double x)
{
double p = a[0];
for (int i = 1; i <= n;i++)
{
p += (a[i]*pow(x,i));
}
return p;
}
double f2(int n, double a[], double x)
{
double p = a[n];
for (int i = n; i > 0;i--)
{
p = a[i - 1] + x * p;
}
return p;
}
测试结果如下图
计算结果表明采用第二种方法计算多项式会使计算时间缩短一个数量级。
注:程序中下划线部分是为了使时间足够大,便于数据准确及数据采集,否则结果如下
结论:解决问题方法的效率和算法的巧妙程度有关
1.1.4 抽象数据类型
问:到底什么是数据结构
1.数据对象在计算机中的组织方式:
-
1.1 逻辑结构:一对一、线性结构;一对多、树型结构;多对多、图型结构;
-
1.2 物理存储结构
2.数据对象必定与一系列加在其上的操作相关联,完成这些操作所用的方法就是算法
抽象数据类型(Abstract Data Type)
数据类型:数据对象集;数据集合相关联的操作集
抽象:描述数据类型的方法不依赖于具体实现。即
1.与存放数据的机器无关;
2.与数据存储的物理结构无关;
3.与实现操作的算法和编程语言无关;
综上所述:抽象即只描述数据对象集合相关操作集“是什么”,不涉及“如何做到”的问题。
简单地类比就是只看函数的输入和输出,不看这个函数是如何实现的具体过程。
例4:“矩阵”的抽象数据类型定义
类型名称:矩阵(Matrix)
数据对象集:一个MxN的矩阵A_(M×N)=(a_ij)(i=1,⋯,M;j=1,⋯,N)由MxN个三元组<a,i,j>构成,其中a是矩阵元素的值,i是元素所在的行号,j是元素所在的列号。
操作集:对于任意矩阵A、B、C∈Matrix,以及整数i、j、M、N
Matrix Create(int M ,int N):返回一个MxN的空矩阵;
int GetMaxRow(Matrix A):返回矩阵A的总行数;
int GetMaxCol(Matrix A):返回矩阵A的总行数;
Matrix Add(Matrix A,Matrix B):如果A和B的行、列数一致,则返回矩阵C=A+B,否则返回错误标志;
抽象的地方例如:
矩阵A_(M×N)是二维数组还是一维数组还是十字链表并未指明,在此处并不关心其是否是哪种类型;
Matrix Add操作是先按行相加还是先按列相加,用什么语言来实现这个操作,在此处并不关心。