文章目录
C++回顾
函数与参数
模板函数
template<class T>
T abc(T a, T b, T c)
return a + b + c;
函数的签名:形参类型及形参个数所确定,如(int,int)。
异常
try{code}
catch(...){code}
try中的代码块抛出异常,进入第一个能捕捉到这种异常类型的catch块,同时其余的catch块被忽略。
动态存储空间分配
操作符new
new的值是一个指针,指向所分配的空间。
// 分配一个整数空间
// 定义y
int* y;
// 动态存储分配
y = new int;
// 为*y赋值
*y = 10;
具体内容可查看动态内存分配。
需要说明的有:
- 存储空间分配分为动态分配和静态分配(定义指针、数组、变量);
- 销毁空间时,指针会变成“”迷途指针“”,务必将其变成零值指针。
另外,我们在使用数组时,并不知道需要其多大,于是,常常会采用为数组分配动态内存的情况。
char* y = new float[n];
操作符delete
动态分配的存储空间不再需要时应该把它释放。
delete *y;
delete []x;
程序性能分析
程序性能指的是运行这个程序需要的内存和时间的多少。可以通过分析方法或实验方法来确定。
空间复杂度
空间复杂度的组成
程序所需要的空间主要由以下部分组成:
- 指令空间:指令空间是指编译之后的程序指令所需要的存储空间;
- 数据空间:所有常量和变量值所需要的存储空间,又分为静态和动态;
- 环境栈空间:环境栈用来保存暂停的函数和方法在恢复运行时所需要的信息,比如函数foo调用了函数goo,需要保存goo结束时函数foo继续执行的指令地址。
指令空间
- 把程序转换成机器代码的编译器;
- 在编译时的编译器选项;
- 目标计算机。
说明:
编译器的优化选项会减少指令的生成(比如对表达式的化简),而覆盖选项可以减少程序空间(多模块共用);
数据空间
环境栈空间
每当一个函数被调用时,返回地址和正在调用的函数的所有局部变量的值以及形式参数的值(该项仅对递归函数而言)会被保存在环境栈中。
分析空间复杂度
程序处理的问题实例都有一些特征,这些特征都包含着可以决定程序空间大小的因素(输入和输出的数量或相关数的大小)。
指令空间的大小受实例特征的影响不大。常量和简单变量所需要的空间与实例特征也没多大关系。
一些动态分配空间也可以不依赖实例特征。环境栈的大小一般也不依赖实例特征,除非用了递归函数。
可以把一个程序所需要的空间分为两部分:
- 固定部分。独立于实例特征。这一部分通常包括指令空间(即代码空间)、简单变量空间和常量空间等;
- 可变部分。由动态分配空间构成和递归栈空间构成。
于是有公式: c + S P ( 实 例 特 征 ) c+S_P(实例特征) c+SP(实例特征)
int factorial(int n){
if(n <= 1) return 1;
else return n* factorial(n-1);
}
首先32位机器的返回地址大小为4字节,形式参数n的大小为4字节,同时,其递归深度为 m a x { n , 1 } max\{n,1\} max{n,1}。所以, S f a c t o r i a l ( n ) = 8 ∗ m a x { n , 1 } S_{factorial}(n)=8*max\{n,1\} Sfactorial(n)=8∗max{n,1}。
所谓实例特征就是影响空间、时间复杂度的特征。
时间复杂度
影响空间复杂度的因素也影响时间复杂度。
一个程序P所需要的时间是编译时间和运行时间之和。编译时间与实例特征无关。一个编译过得程序可以运行若干次而不需要重新编译。
我们所关注的程序的允许时间通常用 t p ( 实 例 特 征 ) t_p(实例特征) tp(实例特征)来表示。
t
p
(
n
)
=
c
a
A
D
D
(
n
)
+
c
s
S
U
B
(
n
)
+
.
.
.
t_p(n)=c_aADD(n)+c_sSUB(n)+...
tp(n)=caADD(n)+csSUB(n)+...
其中c是对应操作需要的时间,ADD、SUB是程序执行该函数的次数。
上述时间复杂度和空间复杂度的公式的表示都是尽可能贴近真实的(单位为秒、字节)。
下面介绍两种比较容易控制的,估算运行时间的分析方法。
操作计数
估算一个程序或函数的时间复杂度,一种方法是选择一种或多种关键操作,然后确定每一种操作的执行次数。使用这种方法成功与否取决于是否能够找到耗时最大的操作。
操作计数是基于特征实例的函数。
因为平均操作计数不易确定,所以通常是分析最好和最坏两种操作计数。
步数
操作计数是针对选定操作而忽视其他操作的。
步数也是实例特征的函数。将对程序/函数的所有操作部分都进行统计。
任何一个实例都有若干个特征(输入个数、输出个数、输入和输出的大小),但是步数是特征的一个子集的函数。
要确定一个程序的步数,首先要确定所采用的实例特征。接着再确定什么是一步。
一个程序步可以定义为一个语法或语义上的程序片段,该片段的执行时间独立于实例特征。