内容来源:浙江大学 数据结构 陈越 何钦铭
1.数据结构的定义
数据结构没有一个统一的定义
以下是《数据结构与算法分析》与中文维基百科中的有关定义:
“数据结构是ADT(抽象数据类型 Abstract Data Type)的物理实现。” ------《数据结构与算法分析》
“数据结构(Data Structure)是计算机中存储、组织 数据的方式。通常情况下,精心选择的数据结构可以 带来最优效率的算法。”
------中文维基百科
可以看到数据结构
和算法
总是在一起的。
如何理解数据结构与算法?
数据结构是数据对象在计算机中的组织方式
- 逻辑结构
- 物理存储结构
其中数据对象必定与一系列加在其上的操作相关联,完成这些操作所用的方法就是算法。
2.算法效率的影响因素
问题分析1:当书规模越趋于大的时候,如何合理的在书架上摆放图书?
所谓合理,即如何有效率的快速找到一本图片,所涉及的操作无非插入新书和查找图书。
在这里,我们可以粗略的给出一种方案:
- 大分类分成不同地小类别,再以小类别为基础再细分;
- 每本书所在的楼号、书架号、书架层号与书名绑定,便于搜索;
- 每个书架层之间按照编号顺序排序,但是如何给每个类别分多少书架感觉需要在之前做一些调查、计算。
因此我们可以得出:
解决问题的效率,跟数据的组织方式有关。
问题分析2:写程序实现一个函数PrintN,使得传入一个正整数为N的参数后,能顺序 打印从1到N的全部正整数
C语言实现
#include <stdio.h>
//Q:实现一个函数,使得传入一个正整数N, 打印出1到N之前的所有整数
/*总结:针对算法一,算法二,当N的值足够大的时候,算法二就会奔溃
原因是递归程序对空间的占用量大,空间不足时,程序就会奔溃;
当然,递归会让程序足够简洁
*/
void Print(int N);
void PrintDG(int N);
//测试
int main(){
int NUM = 1e7;
Print(NUM);
// PrintDG(NUM);
return 0;
}
//算法一 循环实现
void Print(int N){
int I = 1;
while(I <= N){
printf("%d\n",I++);
}
return;
}
//算法二 递归
void PrintDG(int N){
if(N){
PrintDG(N-1);
printf("%d\n",N);
}
return;
}
Java语言实现:
/**
* 测试
* @param args
*/
public static void main(String[] args) {
int N = 10;
printN(N);
// printNDG(N);
}
/**
* 算法一,普通循环
*/
public static void printN(int N){
int i = 1;
while(i<=N){
System.out.println(i++);
}
return;
}
/**
* 算法二,递归实现
*/
public static void printNDG(int N){
if(N != 0){
printNDG(N-1);
System.out.println(N);
}
return;
}
由此可得出:
问题方法的效率, 跟空间的利用效率有关
问题分析3:写程序计算给定多项式在给定点x=1.1 处的值
f
(
x
)
=
1
+
x
+
1
2
x
2
+
⋯
+
1
99
x
99
+
1
100
x
100
f(x) = 1+x + \frac{1}{2}x^2 + \cdots + \frac{1}{99}x^{99} + \frac{1}{100}x^{100}
f(x)=1+x+21x2+⋯+991x99+1001x100
可以通过逐项累加法,循环累加,求出多形式各项的和;还可以用秦九韶算法,通过提公因式,将上述多项式转化为如下式子处理。
f
(
x
)
=
1
+
x
(
1
+
x
(
1
2
⋯
+
x
(
1
99
+
1
100
x
)
)
f(x) = 1 + x(1+x(\frac1{2}\cdots+x(\frac{1}{99}+\frac{1}{100}x))
f(x)=1+x(1+x(21⋯+x(991+1001x))
由于计算机处理太快,为了方便比较两种算法的运行效率,需扩大运算次数
c语言实现:
// Created by xupeng on 2021/1/13.
#include <stdio.h>
#include <math.h>
#include <time.h>
#define COUNT 1e7 /*被测函数运行次数*/
clock_t start, stop;
double duration; /*记录被测函数运行时间,单位为秒*/
double f(double x,double a[],int n);
double f1(double x,double a[],int n);
int main(){
/*测试*/
double x = 1.1; /*变量值*/
double a[100] = {1.0}; /*多项式常数集合*/
int n = 100; /*多项式的最高次数*/
for (int i = 1; i <= 100; i++) {
a[i] = 1.0/i;
}
double res = 0.0;
start = clock();
for (int i = 1; i < COUNT; ++i) {
res = f(x, a, n);
}
stop = clock();
duration = (double)(stop - start)/CLK_TCK/COUNT;
printf("duration: %6.2e\n",duration);
start = clock();
for (int i = 1; i < COUNT; ++i) {
res = f1(x, a, n);
}
stop = clock();
duration = (double)(stop - start)/CLK_TCK/COUNT;
printf("duration: %6.2e\n",duration);
return 0;
}
/**
* @param x 未知数的值
* @param a 多项式系数集合
* @param n 多项式的最高次幂
* @return 多项式值
*/
double f(double x,double a[],int n)
{
double sum = a[0];
for (int i = 1; i <= n; ++i) {
sum += a[i]*pow(x,i);
}
return sum;
}
/**
* 秦九韶算法--提供公因子
将
* @param x 未知数的值
* @param a 多项式系数集合
* @param n 多项式的最高次幂
* @return 多项式值
*/
double f1(double x,double a[],int n)
{
double sum = a[n];
while (n){
sum = a[n-1] + sum*x;
n--;
}
return sum;
}
测试结果:
Java语言实现:
/**
* @author xupeng
* @date 2020/12/22 14:22
* @description
*/
/**
多项式函数:
Q:计算一个多项式在给定x点的值 function -> f,f1
f1的效率比f2慢大概一个数量级的样子,应为f1涉及指数运算,比较耗费时间
Q:
*/
public class Polynomial {
private static int N = 100; // 多项式最高次幂
private static double a[] = new double[101]; //系数集合
private static double X = 1.1; //自编量
private static double COUNT= 1e6; //程序运行的次数
private static long start; //开始时间
private static long stop; //结束时间
/**
* 测试
* @param args
*/
public static void main(String[] args) {
//填充系数集合
for (int i = 0; i <=N ; i++) {
if(i == 0)
a[i] = 1.0;
else
a[i] = 1.0/i;
}
//开始测试
start = System.currentTimeMillis();
double res = 0.0;
for (int i = 0; i <COUNT ; i++) {
res= f1(N, a, X);
}
System.out.println("逐项累加法");
System.out.println("计算结果:" + res);
stop = System.currentTimeMillis();
System.out.println("计算一次的运行时间:" + (stop - start)/COUNT/1000);
start = System.currentTimeMillis();
for (int i = 0; i <COUNT ; i++) {
res = f2(N,a,X);
}
System.out.println();
System.out.println("秦九韶算法");
System.out.println("计算结果:" + res);
stop = System.currentTimeMillis();
System.out.println("计算一次的运行时间:" + (stop - start)/COUNT/1000);
}
/**
* 算法一 单项累加算法
* @param n
* @param a
* @param x
* @return
*/
public static double f1(int n,double a[],double x){
double sum = 0.0;
while(n>=0){
sum += a[n]*Math.pow(x,n--);
}
return sum;
}
/**
* 算法二: 秦九韶算法
* @param n
* @param a
* @param x
* @return
*/
public static double f2(int n,double a[],double x){
double sum = a[n];
while(n > 0){
sum = a[--n] + sum*x;
}
return sum;
}
}
测试结果:
可以看到秦九韶算法比逐项累加算法要快一个数量级的样子,由于逐项累加算法涉及指数运算,相对会比较耗费时间。
因此可得出:
解决问题方法的效率, 跟算法的巧妙程度有关
综上所述,影响解决问题方法的效率和以下三点有关:
- 数据的组织方式
- 算法的空间利用率
- 算法的巧妙程度
3.抽象数据类型
抽象的好处?
抽象,即不依赖数据类型,不依赖物理结构,不依赖实现算法,不依赖编程语言。关心是什么、有什么,不关心怎么做。不考虑怎么做,就不必陷入细节,从概念上勾画出对象,给出数据集和操作集的定义。
抽象能使使得程序具有包容性和广泛适用性。数学就是一种很极致的抽象,所以它可以运用到各种领域,抽象的意义在于找出问题的本质,相当于给了一条解决问题的通路,解决不同的问题只需要在原有基础上做出相应调整而不用再去重新研究,这样会大大提高效率。
案例代码