算法是解题方案,不等于程序,不等于计算方法。
对数据的运算和操作 | 算法的控制结构 |
---|---|
算数运算(加减乘除) | 顺序 |
逻辑运算 (与或非) | 分支 |
关系运算 (大于、小于、等于) | 循环 |
数据的传输(赋值、输入输出等) | - |
1、列举法 不实用
问题:母鸡3钱、公鸡2钱、鸡仔0.5钱。百钱百鸡,问各多少只?
列举法思路:
{ x + y + z = 100 3 x + 2 y + 0.5 z = 100 \begin{cases} x+y+z =100 \\ 3x+2y+0.5z = 100 \end{cases} {x+y+z=1003x+2y+0.5z=100
推导:
x , y , z ∈ [ 0 , 100 ] , { x , y , z 都是正整数 } x,y,z \in [0,100],\{\ x,y,z都是正整数 \}\ x,y,z∈[0,100],{ x,y,z都是正整数}
列举出所有情况,代码如下所示:
#include
#define N 100
int x,y,z;
void main(void){
for(x=0;x<N;x++){
for(y=0;y<N;y++){
for(z=0;z<N;z++){
if((x+y+z == 100) && (3x+2y+0.5z == 100)){
printf("母鸡有%d只,公鸡有%d只,鸡仔有%d只",x,y,z);
}
}
}
}
}
2、归纳法
3、递推法
4、递归
问题:给出一个正整数 n ,打印出1~n 的所有正整数。
代码如下:
#include
void write(int n){
if(n == 0){
printf("0\n");
}else{
printf("%d\n",n);
write(n-1);
}
}
void main(){
write(10);
}
5、减半递推技术
6、回溯法
其实就是几种方法齐上阵,因为很多问题不能在只用一种方法的情况下解决。
影响因素:
时间复杂度 | 空间复杂度 |
---|---|
程序语言 | 算法程序所占的空间 |
编译产生的机器语言,代码质量 | 输入的初始数据所占的存储空间 |
机器指令的执行速度 | 算法执行过程中所要的额外空间 |
问题的规模(主要因素) |
算法的工作量 = f(n)
1、算法中基本操作重复执行的频率 T(n) ,是问题规模 n 的某个函数 f(n) ,
记作:
T ( n ) = O ( f ( n ) ) T(n)=O(f(n)) T(n)=O(f(n))
tips:随着问题规模 n 的增加,算法执行时间的增长率和 f(n) 相应增加。
常见算法的复杂度
复杂度符号表示方法 | 名称 |
---|---|
O(1) | 常数阶 |
O(n) | 线性阶 |
O(n2) | 平方阶 |
O(n3) | 立方阶 |
O(log(n)) | 对数阶 |
O(2n) | 指数阶 |
示例:n*n矩阵相乘算法,三重循环 时间复杂度为 O(n3)
#include
//已知a,b两个n阶方阵,求 a和b的乘积,结果保存在 c 中
void Mult_matrix(int c[][],int a[][],int b[][],int n)
{
for(int i=0;i<n;i++){//循环**n**次
for(int j=0;j<n;j++){//循环**n**次
for(int k=0;k<n;k++){//循环**n**次
c[i,j] += a[i,k]*b[k,j];
}
}
}
}
分析算法工作量的两种方法
算法执行过程中所需要的最大存储空间
算法的存储量包括以下三个部分:
空间复杂度定义:
S ( n ) = O ( f ( n ) ) S(n) = O(f(n)) S(n)=O(f(n))
原地工作(in place)的算法:记作: O ( 1 ) O(1) O(1)
压缩存储技术
相互有关联的数据元素的集合;数据元素之间的关系可以用前后件关系来描述。
一个数据结构应该包含以下两方面信息:
数据结构研究的主要内容 | 研究数据结构的目的 |
---|---|
数据的逻辑结构 | 时间上:提高数据处理的速度 |
数据的存储结构 | 空间上:尽量节省在数据处理过程中所占用的存储空间 |
对各种数据结构进行的运算 |
一个数据结构B可以表示为: B = ( D , R ) B=(D,R) B=(D,R)
概念:数据的逻辑结构在计算机存储空间中的存放形式
常用的存储结构:
基本运算:
**空的数据结构:**一个数据元素都没有
满足以下两个条件:
常见的非线性结构:
由n个相同类型的数据元素构成的有限序列
例如:
结构特征:
线性表的存储结构:
两个基本特点:
向位置 i 插入一个元素,需要将位置 i+1 ~ n 的所有元素后移一位。
分析:
假设线性表中含有 n 个数据元素,在进行插入操作时,假定在 n+1 个位置插入元素的可能性均等,那么平均移动元素的个数为: E i s = 1 n + 1 ∑ i = 1 n + 1 p i ( n − i + 1 ) = n 2 E_{is}=\frac {1}{n+1}\sum_{i=1}^{n+1} {p_i(n-i+1)}=\frac{n}{2} Eis=n+11i=1∑n+1pi(n−i+1)=2n
分析:
假设线性表中含有 n 个数据元素,在进行删除操作时,假定在每个位置删除元素的可能性均等,那么平均移动元素的个数为: E d l = 1 n ∑ i = 1 n p i ( n − 1 ) = n − 1 2 E_{dl}=\frac {1}{n}\sum_{i=1}^{n} {p_i(n-1)}=\frac{n-1}{2} Edl=n1i=1∑npi(n−1)=2n−1
分析结论:
顺序存储结构的线性表,在做插入或者删除操作时,平均需要移动一半数据元素,当线性表的数据元素量较大,并且经常要对其做插入和删除操作时,这一点值得考虑。
栈和队列是特殊的线性表
定义:栈(stack):只允许在一端(栈顶)进行插入和删除操作的特殊线
性表。
运算:
定义: 限定只能在表的一端插入,在另一端进行删除的线性表
例子:排队做核酸
基本运算:
循环队列运算: 队头咬队尾
不重复地访问二叉树中的所有节点
从表中第一个元素开始,逐个比较,直到两者相符。否则就是查找不成功。
平均和一般元素比较,最坏的情况比较n次。
以下情况必须顺序查找
先确定范围,再缩小范围,直到找到或者找不到。
前提:
特点:
基本思想: 两两比较排序记录的关键字,发现两个记录的次序相反时即进行交换,直到没有反序为止
方法:
冒泡排序性能分析
假设线性表长度为n,最坏情况下,需要比较次数: n ( n − 1 ) / 2 n(n-1)/2 n(n−1)/2
快速排序
基本思想:任取一个元素为基准,通过一趟排序,分为左右两个子序列。然后分别对两个子序列继续进行排序,直至整个序列有序。
基本思想: 每次将一个待排序的记录,按照其关键字大小插入到前面已经排好序的子序列的适当位置,直到全部记录插入完成为止。
方法:
基本思想: 把n个待排序的元素看成为一个有序表和一个无序表。开始的时候有序表只有一个元素,排序过程中每次从无序表中取第一个元素,把他的排序码依次和有序表的排序码进行比较,将它插入到有序表的适当位置,使之成为新的有序表。
最坏情况下,需要进行比较次数为: n ( n − 1 ) / 2 n(n-1)/2 n(n−1)/2
基本思想: 本质上也是插入排序。先将整个序列分为若干个子序列(由相隔某个增量 h的元素组成的),分别进行直接插入排序,等整个序列元素基本有序,再对全体元素进行一次直接插入排序。直接插入排序再元素基本有序的情况下,效率非常高
增量一般取 h i = n / 2 k ( k = 1 , 2 , . . . , [ l o g 2 n ] ) h_i = n/2^k (k=1,2,...,[log_2n]) hi=n/2k(k=1,2,...,[log2n])
最坏情况下: 希尔排序的时间复杂度为: O ( n 1.5 ) O(n^{1.5}) O(n1.5)
基本思想: 每一趟选出最小值,顺序放在已经排好序的子序列最后,直到排序完成
方法: