目录
3.1 算法的定义:是对特点问题求解步骤的一种描述,是指令的有限序列。
第一题求1~n的连续整数的和,并比较两个算法(累加算法和公式法)的优劣
第二题:编写一个程序对于1~n的每一个整数n,输出,,n,n、,,n!的图像
一.数据结构的定义
1.1 用计算机解决一个问题大致需要经过以下几个步骤
- 分析问题,确定数据模型
- 设计相应的算法
- 编写程序,运行并调试程序,直到出现正确的答案
1.2 数据、数据项、数据元素、数据对象、数据结构
数据:是描述客观事物的数和字符的集合。例如生活中使用的文字、数字、字符。从计算机角度看,数据是所有能被输入到计算机中,且能被计算机处理的符号的集合。数据元素:是数据的基本单位(如200902班的每个学生的记录是一个数据元素)。
数据项:是具有独立含义的数据最小单位,也称为字段或域。如(200902班中每个数据元素(即学生记录)是由学号、姓名、性别、班号等数据项组成)
数据对象:是指性质相同的数据元素的集合,他是数据的一个子集。在数据结构中讨论的数据通常指的是数据对象。
数据结构:是指所有数据元素以及元素之间的关系,可以看作相互之间存在者某种特定关系的数据元素的集合
1.3 数据结构由以下三个组成
1.3.1数据的逻辑结构:逻辑结构与数据存储无关,是独立于计算机的,因此数据的逻辑结构可以看成 从具体问题抽象出来的数据模型。
- 逻辑结构的表示:图表表示、二元组表示(B=(D、R),B:数据元素的集合、R:关系的集合)
- 逻辑结构的类型:集合(无关系)、线性(1对1)、树(1对多)、图或网(多对多)
- 线性结构:数组、队列、线性表链表、栈
- 非线性结构:树、图、广义表、二维数组
1.3.2 数据的物理结构:顺序存储结构和物理存储结构
1.3.3 运算:实施在数据上的操作
1.4 存储结构
1.4.1 顺序和链式
- 顺序存储结构:是采用一组连续的存储单元存放所有数据元素,所有的数据元素在存储器中占有一整块存储空间,而且两个逻辑上相邻的数据元素在物理上也相邻。即顺序存储结构将数据的逻辑结构直接映射到存储单元。
- 物理存储结构:每个逻辑元素用一个内存结点存储,每个节点是单独分配的,所有结点地址不一定是连续的,为了表示元素之间的逻辑关系,给每个结点附加指针域,用来存放逻辑地址,通过指针域将结点连接起来。
- 优点\缺点:
- 顺序存储:
- 优点:①存储效率高,因为分配给数据的存储空间全部用于存放数据元素,元素之间的逻辑关系没有占用额外的存储空间。②顺序存储,随机存取。
- 缺点:①不便修改数据,插入、删除不方便需要移动结点时间复杂度高。
- 链式存储:
- 优点:①方便数据修改:对于元素的插入和删除只用修改相应结点的指针域,不用移动结点。
- 缺点:①空间利用率低:因为要分配一部分空间来用来存储逻辑关系。②因为逻辑上相邻的元素在物理上不一定相邻,不能随机存取。
1.4.2 索引和哈希(散列)存储结构
索引存储结构:是在存储数据元素信息的同时还建立附加的索引表。存储的数据元素信息的表称为主数据表,其中每个数据元素有一个关键字还有对应的存储地址。优点:效率高。缺点:耗空间。
哈希存储结构:根据元素的关键字通过哈希(散列)函数计算一个值,将这个值作为该元素的存储地址。
二 数据类型和抽象数据类型
2.1 C语言的数据类型以及大小
2.1.1 静态分配和动态分配
静态;int a【10】
动态:p=(char*)malloc(10*sizeof(char));
2.2 抽象数据类型
- 抽象数据类型(ADT)是用户抽象出来的逻辑结构和逻辑结构上的运算,不用考虑计算机具体存储结构和运算的具体实现算法。
- 由三元组组成:(D,R,P)D:数据对象。R:关系的集合。P:是数据运算的基本运算集。
- 抽象数据类型的特征是:数据抽象、数据封装
- 一个求解问题可以通过抽象数据类型来描述,抽象数据类型对一个求解问题从逻辑上进行了准确的定义,所以其由数据的逻辑结构和运算组成。
- 抽象数据类型可以定义一个完整的数据结构。
三 . 算法定义
3.1 算法的定义:是对特点问题求解步骤的一种描述,是指令的有限序列。
3.2 算法的特性(必要条件不是充分条件)
①有穷性
②确定性(无二义性):在任何条件下算法只有一条执行路径,相同的输入只有得出相同的输出,无二义性。
③可行性:每个算法都可以由有限次基本操作完成
④输入:0个或多个输入
⑤输出:1个或多个输出
3.3 算法的标准
① 正确性:最重要、最基本的标准
② 可使用性:要求算法能更方便使用,又叫用户友好性
③ 可读性:
④ 健壮性:具有很好的容错性,提供异常处理,对不合理的数据进行检测,不经常出现异常中断或死机等现象
⑤ 高效率以的低存储量需求:
四 算法分析
4.1 算法的时间分析
- 事后统计法:编写算法的对应程序,统计其执行时间。
- 缺点:一是必须执行程序,二是存在很多因素掩盖了算法的本质
2. 事前分析法:撇开与计算机硬件、软件有关的因素,仅考虑算法本身的效率高低,可以认定一个特定算法的运行工作量的大小只依赖于问题的规模(通常用n表示),或者说算法的执行时间是问题规模的函数。
4.2 算法的时间复杂度与空间复杂度
4.2.1 问题规模和语句频度
问题规模:算法求解问题输入量的多少 ,是问题大小的本质表示,一般用整数n表示
语句频度:一条语句的重复执行次数
例题
for(i=1;i<=n;i++) //语句频度 n+1
for(j=1;j<=n;j++) //n*(n+1)
{
c[i][j]=0; //n^2
for(k=1;k<=n;k++) //n^2(n+1)
c[i][j]=c[i][j]+a[i][k]*b[k][j]; //n^3
}
算法的时间复杂度取决于:问题的规模和待处理数据的初态
常见的时间复杂度:常队幂指阶
4.2.2 空间复杂度
一个算法在运行过程中临时占用的存储空间的大小的度量。
若所需临时空间大小相对于问题规模来说是常数,这称这个算法原地工作
练习题
代码题
第一题求1~n的连续整数的和,并比较两个算法(累加算法和公式法)的优劣
方法一:累加法
clock的用法
在C语言中,clock函数用于测量程序执行的时间,返回程序自启动以来所消耗的处理器时间,以时钟周期数表示。这个函数定义在<time.h>头文件中,通过结合CLOCKS_PER_SEC宏常量,可以将返回的时钟周期数转换为秒,从而计算出实际的运行时间。
clock函数的常见用法是计算特定代码段的执行时间,适用于性能分析和优化。使用时,首先调用clock记录起始时间,然后执行需要测量的代码,最后再调用clock记录结束时间,并通过计算差值得到总执行时间。
clock_t t; //定义时钟变量t
t = clock(); //用来生成调用函数之前的时间t = clock() - t; //求时间差,即求该函数的执行时间
printf("用时:%lf秒\n",((float)t)/CLOCKS_PRE_SEC); //
CLOCKS_PER_SEC
宏常量,可以将返回的时钟周期数转换为秒,从而计算出实际的运行时间。
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include <math.h>
#include<time.h>
//累加法
int add(int n)
{
int sum = 0;
printf("请输入n的值");
scanf("%d", &n);
for (int i = 0; i <= n; i++)
{
sum = sum + i;
}
return sum;
}
int main()
{
int n = 0;
clock_t t;
t = clock();
printf("n的和是%d\n", add(n));
t = clock() - t;
printf("用时:%lf秒\n", ((float)t)/CLOCKS_PER_SEC);
return 0;
}
方法二:公式法
//公式法
int fun(int n)
{
printf("请输入n的值");
scanf("%d", &n);
return n*(n + 1) / 2;
}
int main()
{
int n = 0;
clock_t t;
t = clock();
printf("n的和是%d\n", fun(n));
t = clock() - t;
printf("耗时%d\n", t);
return 0;
}
由此可见第二种算法的时间优势大
第二题:编写一个程序对于1~n的每一个整数n,输出,,n,n、,,n!的图像
各个数据类型的占位符介绍
在C语言中,不同数据类型在打印时使用不同的占位符,同时它们占用的字节数也有所不同。以下是常见的基本数据类型及其对应的打印占位符和所占字节数:
int
- 打印占位符:
%d
或%i
- 占用字节数:通常是4字节(具体取决于系统和编译器)
float
- 打印占位符:
%f
- 占用字节数:4字节
double
- 打印占位符:
%lf
- 占用字节数:8字节
long
- 打印占位符:
%ld
或%li
- 占用字节数:通常是8字节(具体取决于系统和编译器)
char
- 打印占位符:
%c
- 占用字节数:1字节
所以这里5.2f 的意思是,总宽度为5个字符,小数点后保留两位
%2d的意思是数字按宽度为2输出,不足2的左边补空格;
% 02d的意思与%2d差不多,只不是是左边补0;
\t在这里的意思是水平制表符,相当于tab
常见的转义字符
在C语言中,转义字符用于在字符串或字符常量中表示一些特殊的字符或控制字符。这些转义字符以反斜杠(\
)开头,常见的转义字符及其含义如下:
\n
:换行符,用于将光标移动到下一行的开头。\t
:水平制表符,用于水平跳到下一个制表位。\r
:回车符,将光标移到当前行的开头。\b
:退格符,将光标移到前一列位置(通常用于删除前一个字符)。\f
:换页符,将光标移到下一页的开头。\a
:响铃符,触发计算机的警报声(蜂鸣)。\v
:垂直制表符,用于垂直跳到下一个制表位。\\
:反斜杠字符,用于表示一个反斜杠。\'
:单引号字符,用于表示一个单引号。\"
:双引号字符,用于表示一个双引号。\?
:问号字符,用于表示一个问号。\0
:空字符(NUL),表示字符串的结束标志。
double log2(double n)
{
return log10(n) / log10(2);
}
long exponent(long n)
{
int num = 1;
for (int i = 1; i <= n; i++)
{
num = num * 2;
}
return num;
}
long fac(long n)
{
int num = 1;
for(int i = 1; i <= n; i++)
{
num = num * n;
}
return num;
}
void fun(int n)
{
printf("log2(n) sqrt() n nlog2(n) n^2 n^3 2^n n^n\n");
printf("=======================================================\n");
for(int i =1;i<=n;i++)
{
printf("%5.2f\t", log2(double(i))); //求对数
printf("%5.2f\t", sqrt(i)); // sqrt用来求开方
printf("%2d\t", i); //求n
printf("%7.2f\t", i * log2(double(i))); //求n*log2n
printf("%5d\t", i * i);
printf("%7d\t", i * i * i);
printf("%8d\t", exponent(i)); //求幂函数
printf("%10d\t", fac(i)); //求阶乘
}
}
int main()
{
int n = 10;
fun(n);
}
第三题:
给你一个 32 位的有符号整数
x
,返回将x
中的数字部分反转后的结果。如果反转后整数超过 32 位的有符号整数的范围
[−231, 231 − 1]
,就返回 0。