--- config: look: handDrawn themeVariables: fontSize: 28px --- flowchart LR fibnacci --> fibnacci
《计算机程序设计》
计算机学院计算机研究所编译系统研究室
~/course
~/course/main.cpp
main.cpp
,随堂编程练习的代码请直接在此文件中编辑文件读写
#include <fstream>
#include <iostream>
using namespace std;
int main() {
int arr[5] = {1, 2, 3, 4, 5};
ofstream fout("data.txt");
for (int i = 0; i < 5; i++)
fout << arr[i] << ' ';
fout.close();
ifstream fin("data.txt");
int value;
while (not fin.eof()) {
fin >> value;
cout << value << ' ';
}
return 0;
}
学习目标
\[ F(n) = \begin{cases} 1 & n < 2 \\ F(n-1) + F(n-2) & n \ge 2 \end{cases} \]
--- config: look: handDrawn themeVariables: fontSize: 28px --- flowchart LR fibnacci --> fibnacci
考考你
阶乘公式可否具有自相似性?可否通过递归方式定义?
\[ sum(n) = \begin{cases} 0 & n = 0 \\ n + sum(n-1) & n > 0 \end{cases} \]
\[ n! = \begin{cases} 1 & n = 0 \\ n \times (n-1)! & n > 0 \end{cases} \]
\[ F(n) = \begin{cases} 1 & n < 2 \\ F(n-1) + F(n-2) & n \ge 2 \end{cases} \]
明察秋毫
\[ F(n) = \begin{cases} 1 & n < 2 & \Leftarrow \text{边界问题}\\ F(n-1) + F(n-2) & n \ge 2 & \Leftarrow \text{递推公式} \end{cases} \]
递归函数的构造模板
考考你
编写一个递归函数,计算n!
的值
factorial.cpp
#include <cstdlib>
#include <iostream>
using namespace std;
int factorial(int n) {
if (n == 0) {
return 1;
}
int subfactorial = factorial(n - 1);
cout << n << " &n=" << &n << ", &subfactorial=" << &subfactorial << endl;
return n * subfactorial;
}
int main(int argc, char *argv[]) {
int n = atoi(argv[1]);
int f = factorial(n);
cout << f << '\n';
return 0;
}
1 &n=0x7ffd776221bc, &subfactorial=0x7ffd776221c4
2 &n=0x7ffd776221ec, &subfactorial=0x7ffd776221f4
3 &n=0x7ffd7762221c, &subfactorial=0x7ffd77622224
6
考考你
上述程序的输出结果说明了什么现象?
factorial.cpp
#include <cstdlib>
#include <iostream>
using namespace std;
int factorial(int n) {
if (n == 0) {
return 1;
}
int subfactorial = factorial(n - 1);
cout << n << " &n=" << &n << ", &subfactorial=" << &subfactorial << endl;
return n * subfactorial;
}
int main(int argc, char *argv[]) {
int n = atoi(argv[1]);
int f = factorial(n);
cout << f << '\n';
return 0;
}
factorial.cpp
#include <cstdlib>
#include <iostream>
using namespace std;
int factorial(int n) {
if (n == 0) {
return 1;
}
int subfactorial = factorial(n - 1);
cout << n << " &n=" << &n << ", &subfactorial=" << &subfactorial << endl;
return n * subfactorial;
}
int main(int argc, char *argv[]) {
int n = atoi(argv[1]);
int f = factorial(n);
cout << f << '\n';
return 0;
}
factorial.cpp
#include <cstdlib>
#include <iostream>
using namespace std;
int factorial(int n) {
if (n == 0) {
return 1;
}
int subfactorial = factorial(n - 1);
cout << n << " &n=" << &n << ", &subfactorial=" << &subfactorial << endl;
return n * subfactorial;
}
int main(int argc, char *argv[]) {
int n = atoi(argv[1]);
int f = factorial(n);
cout << f << '\n';
return 0;
}
factorial.cpp
#include <cstdlib>
#include <iostream>
using namespace std;
int factorial(int n) {
if (n == 0) {
return 1;
}
int subfactorial = factorial(n - 1);
cout << n << " &n=" << &n << ", &subfactorial=" << &subfactorial << endl;
return n * subfactorial;
}
int main(int argc, char *argv[]) {
int n = atoi(argv[1]);
int f = factorial(n);
cout << f << '\n';
return 0;
}
factorial.cpp
#include <cstdlib>
#include <iostream>
using namespace std;
int factorial(int n) {
if (n == 0) {
return 1;
}
int subfactorial = factorial(n - 1);
cout << n << " &n=" << &n << ", &subfactorial=" << &subfactorial << endl;
return n * subfactorial;
}
int main(int argc, char *argv[]) {
int n = atoi(argv[1]);
int f = factorial(n);
cout << f << '\n';
return 0;
}
factorial.cpp
#include <cstdlib>
#include <iostream>
using namespace std;
int factorial(int n) {
if (n == 0) {
return 1;
}
int subfactorial = factorial(n - 1);
cout << n << " &n=" << &n << ", &subfactorial=" << &subfactorial << endl;
return n * subfactorial;
}
int main(int argc, char *argv[]) {
int n = atoi(argv[1]);
int f = factorial(n);
cout << f << '\n';
return 0;
}
factorial.cpp
#include <cstdlib>
#include <iostream>
using namespace std;
int factorial(int n) {
if (n == 0) {
return 1;
}
int subfactorial = factorial(n - 1);
cout << n << " &n=" << &n << ", &subfactorial=" << &subfactorial << endl;
return n * subfactorial;
}
int main(int argc, char *argv[]) {
int n = atoi(argv[1]);
int f = factorial(n);
cout << f << '\n';
return 0;
}
考考你
如果递归调用的深度过深,会发生什么?
明察秋毫
函数调用的实现机制可以保证统一函数的不同调用栈空间隔离,但递归本身会引入较多的函数调用开销
考考你
假设一对成熟的兔子每月能生一对小兔(一雌一雄),小兔一个月后长为成熟兔子,假定兔子不死亡,那么由一对刚出生的小兔开始,3年后有多少对兔子?
科学考古
1202年,意大利数学家斐波那契提出了“兔子繁殖问题”,这就是斐波那契数列的由来
考考你
假设小明每一步可以登上一级、两级或者三级台阶,问小明有多少种不同方式登上n个台阶?
13
考考你
能不能使用递归的思路实现排序算法?思路:选择一个元素将之放置到正确的位置上,然后对剩余的元素进行排序
selectsort.cpp
#include <cstdlib>
#include <iostream>
#include <chrono>
using namespace std;
void select_sort(int a[], int i, int n);
int main(int argc, char *argv[]) {
int n = atoi(argv[1]);
int *a = new int[n];
for (int i = 0; i < n; i++)
a[i] = rand() % RAND_MAX;
auto start = chrono::system_clock::now();
select_sort(a, 0, n);
auto end = chrono::system_clock::now();
chrono::duration<double> elapsed_seconds = end - start;
cout << "elapsed time: " << elapsed_seconds.count() << "s\n";
delete[] a;
return 0;
}
考考你
能不能使用递归的思路实现排序算法?思路:选择一个元素将之放置到正确的位置上,然后对剩余的元素进行排序
quicksort.cpp
#include <cstdlib>
#include <iostream>
#include <chrono>
using namespace std;
void quicksort(int a[], int i, int j);
int main(int argc, char *argv[]) {
int n = atoi(argv[1]);
int *a = new int[n];
for (int i = 0; i < n; i++)
a[i] = rand() % RAND_MAX;
auto start = chrono::system_clock::now();
quicksort(a, 0, n);
auto end = chrono::system_clock::now();
chrono::duration<double> elapsed_seconds = end - start;
cout << "elapsed time: " << elapsed_seconds.count() << "s\n";
delete[] a;
return 0;
}
quicksort.cpp
void quicksort(int a[], int i, int j) {
if (i + 1 == j) {
if (a[i] > a[j]) swap(a[i], a[j]);
return;
}
int middle = a[(i + j) / 2];
int left = i, right = j;
while (left <= right) {
while (a[left] < middle) left++;
while (a[right] > middle) right--;
if (left <= right) {
swap(a[left], a[right]);
left++;
right--;
}
}
if (i < right) quicksort(a, i, right);
if (left < j) quicksort(a, left, j);
}
考考你
选择排序与快速排序,你觉得哪个更快?
$ g++ -o code/selectsort code/selectsort.cpp
$ g++ -o code/quicksort code/quicksort.cpp
$ ./code/selectsort 1000; ./code/selectsort 10000; ./code/selectsort 100000
$ ./code/quicksort 1000; ./code/quicksort 10000; ./code/quicksort 100000
elapsed time: 0.000477592s
elapsed time: 0.0468432s
elapsed time: 4.5469s
elapsed time: 8.1016e-05s
elapsed time: 0.000843884s
elapsed time: 0.0112795s
在印度,有一个古老的传说:布拉马圣殿有三根金刚石柱子,第一根柱子上套放了64个金盘,每个金盘都比其下面的金盘略小。教士们预言:当按预定的规则,把第一根柱子上的金盘全部移到第三根柱子上时世界末日就会来临
默认参数
明察秋毫
defaultarg.cpp
code/defaultarg.cpp:10:5: error: default argument given for parameter 2 of ‘int func(int, int, int)’ [-fpermissive]
10 | int func(int a, int b = 10, int c = 20) {
| ^~~~
code/defaultarg.cpp:3:5: note: previous specification in ‘int func(int, int, int)’ here
3 | int func(int a, int b = 10, int c = 20);
| ^~~~
code/defaultarg.cpp:10:5: error: default argument given for parameter 3 of ‘int func(int, int, int)’ [-fpermissive]
10 | int func(int a, int b = 10, int c = 20) {
| ^~~~
code/defaultarg.cpp:3:5: note: previous specification in ‘int func(int, int, int)’ here
3 | int func(int a, int b = 10, int c = 20);
| ^~~~
考考你
以下是C标准库中的浮点绝对值函数(在math.h
头文件中声明),看一看有没有令人不满意的地方?
不足之处
三个函数功能一致,仅因为作用在不同精度浮点类型上,就需要不同的函数名予以区分
<cmath>
,即可对任意精度的浮点数调用fabs()
函数考考你
下面的函数重载是否正确?
明察秋毫
函数重载不允许仅通过返回值区分同名函数!
考考你
下面程序是否存在问题?
code/funcquiz.cpp: In function ‘int main()’:
code/funcquiz.cpp:7:15: error: call of overloaded ‘func(int)’ is ambiguous
7 | cout << func(1) << endl;
| ~~~~^~~
code/funcquiz.cpp:3:5: note: candidate: ‘int func(int, int)’
3 | int func(int a, int b = 1) { return a + b; }
| ^~~~
code/funcquiz.cpp:4:5: note: candidate: ‘int func(int)’
4 | int func(int a) { return a; }
| ^~~~
code/funcquiz.cpp:8:15: error: call of overloaded ‘func(int, int)’ is ambiguous
8 | cout << func(1, 2) << endl;
| ~~~~^~~~~~
code/funcquiz.cpp:3:5: note: candidate: ‘int func(int, int)’
3 | int func(int a, int b = 1) { return a + b; }
| ^~~~
code/funcquiz.cpp:5:5: note: candidate: ‘int func(int, int, int)’
5 | int func(int a, int b, int c = 1) { return a + b + c; }
| ^~~~
避坑
--- config: look: handDrawn themeVariables: fontSize: 24px --- mindmap 函数进阶 默认参数 函数重载 二义性问题 递归函数 递归的概念 递归基本原理(二要素) 递归函数调用过程 递归函数应用 数列求解 上台阶问题 排序问题 汉诺塔问题
学习目标
计算机程序设计