数据结构实验1
实验题1:求1到n的连续整数和
题目描述
编写一个程序,对于给定的正整数n,求1+2+…十n,采用逐个累加与(n+1)/2(高斯法)两种解法。对于相同的n,给出这两种解法的求和结果和求解时间,并用相关数据进行测试。
运行代码
//实验题1:求1到n的连续整数和
#include<iostream>
#include<time.h>
#include<stdio.h>
#include<math.h>
using namespace std;
#define ll long long
ll add1(ll n) {
ll sum = 0;
for (ll i = 1; i <= n; i++) {
sum += i;
}
return sum;
}
void AddTime1(ll n) {
clock_t t;
ll sum;
t = clock();
sum = add1(n);
t = clock() - t;
cout << "方法一:" << endl;
printf(" 结果:1 到 %lld 之和:%lld\n", n, sum);
printf(" 用时:%lf 秒\n", ((double)t) / CLOCKS_PER_SEC);
}
ll add2(ll n) {
return n * (n + 1) / 2;
}
void AddTime2(ll n) {
clock_t t;
ll sum;
t = clock();
sum = add2(n);
t = clock() - t;
cout << "方法二:" << endl;
printf(" 结果:1 到 %lld 之和:%lld\n", n, sum);
printf(" 用时:%lf 秒\n", ((double)t) / CLOCKS_PER_SEC);
}
int main() {
ll n;
printf("n(大于 1000000):");
cin >> n;
if (n < 1000000) return 0;
AddTime1(n);
AddTime2(n);
return 0;
}
代码思路
- 头文件和命名空间
- 包含了必要的输入输出流头文件
<iostream>
、时间相关头文件<time.h>
、标准输入输出头文件<stdio.h>
和数学库头文件<math.h>
。 - 使用
using namespace std
引入标准命名空间。 - 定义宏
ll
为long long
,方便后续使用长整型数据类型。
- 包含了必要的输入输出流头文件
- 函数定义
add1
函数:使用循环遍历从 1 到n
的整数,逐个累加求和。AddTime1
函数:测量add1
函数的执行时间,并输出结果和用时。add2
函数:利用数学公式n*(n+1)/2
直接计算从 1 到n
的整数之和。AddTime2
函数:测量add2
函数的执行时间,并输出结果和用时。
main
函数- 从用户输入获取整数
n
,要求n
大于 1000000,否则程序退出。 - 分别调用
AddTime1
和AddTime2
函数,对两种方法进行测试。
- 从用户输入获取整数
-
方法一的原理:循环遍历从 1 到
n
的每个整数,逐个累加到变量sum
中,最终得到从 1到n
的整数之和。这种方法直观易懂,但当n
较大时,循环执行次数较多,可能会比较耗时。 -
方法二的原理:利用数学公式
1 + 2 + 3 +... + n = n*(n+1)/2
直接计算从 1 到n
的整数之和。这个公式可以通过数学归纳法证明。这种方法避免了循环遍历,计算效率更高,尤其是对于较大的n。
通过测量两种方法的执行时间,可以看出当 n
较大时,方法二通常比方法一执行速度更快,因为方法二避免了大量的循环迭代操作,直接利用数学公式进行计算。
实验题2:常见算法时间函数的增长趋势分析
题目描述
运行代码
//实验题2:常见算法时间函数的增长趋势分析
#include <iostream>
#include <cmath>
using namespace std;
double log2(double n) {
return log10(n) / log10(2);
}
int factorial(int n) {
if (n == 0 || n == 1)
return 1;
else
return n * factorial(n - 1);
}
int main() {
cout << "n\tlog2(n)\t√n\tn\tn^2\tn^3\t2^n\tn!" << endl;
for (int n = 1; n <= 10; n++) {
cout << n << "\t" << log2(n) << "\t" << sqrt(n) << "\t" << n << "\t" << n * n << "\t" << n * n * n << "\t" << pow(2, n) << "\t" << factorial(n) << endl;
}
return 0;
}
代码思路
一、代码思路
- 函数定义
log2
函数:通过换底公式logₐb = logₑb / logₑa
,这里使用以 10 为底的对数函数log10
计算出以 2 为底的对数。factorial
函数:使用递归的方式计算给定整数n
的阶乘。当n
为 0 或 1 时,阶乘为 1;否则,n
的阶乘等于n
乘以n - 1
的阶乘。
main
函数- 首先输出表头,展示不同算法时间函数的名称。
- 使用循环从 1 到 10 遍历整数
n
。 - 在循环中,分别计算并输出
n
、以 2 为底n
的对数、n
的平方根、n
、n
的平方、n
的立方、2 的n
次方、n
的阶乘的值。
二、原理
log2(n)
:对数函数的增长非常缓慢。随着n
的增大,对数函数的值增长速度远远慢于线性增长。√n
:平方根函数的增长速度比线性增长慢,但比对数函数快。n
:代表线性增长,即随着n
的增加,函数值以相同的比例增加。n^2
:二次函数的增长速度比线性增长快得多。当n
增大时,二次函数的值增长迅速。n^3
:三次函数的增长速度比二次函数更快。2^n
:指数函数的增长速度非常快,远远超过其他函数。n!
:阶乘函数的增长速度是所有这些函数中最快的。随着n
的增大,阶乘函数的值呈爆炸式增长。
通过输出这些不同函数在不同n
值下的结果,可以直观地观察到它们的增长趋势差异,对于分析算法的时间复杂度非常有帮助。例如,在选择算法时,如果一个算法的时间复杂度是指数级的(如2^n
或n!
),那么对于较大的输入规模,该算法可能会非常耗时,而如果算法的时间复杂度是线性的(如n
)或对数级的(如log2(n)
),则通常会更加高效。
实验题3:求素数的个数
题目描述
编写一个程序,求1~n的素数个数。给出两种解法,对于相同的n,给出这两种解法的结果和求解时间,并用相关数据进行测试。
运行代码
//实验题3:求素数的个数
#include <iostream>
#include <ctime>
using namespace std;
bool isPrime1(int n) {// 方法 1:传统方法判断素数,时间复杂度 O(n)
if (n < 2) return false;
for (int i = 2; i < n; i++) {
if (n % i == 0) return false;
}
return true;
}
int prime1(int n) {
int count = 0;
for (int i = 2; i <= n; i++) {
if (isPrime1(i)) count++;
}
return count;
}
bool isPrime2(int n) {// 方法 2:改进方法判断素数,时间复杂度 O(√n)
if (n < 2) return false;
if (n == 2 || n == 3) return true;
if (n % 2 == 0 || n % 3 == 0) return false;
for (int i = 5; i * i <= n; i += 6) {
if (n % i == 0 || n % (i + 2) == 0) return false;
}
return true;
}
int prime2(int n) {
int count = 0;
for (int i = 2; i <= n; i++) {
if (isPrime2(i)) count++;
}
return count;
}
void PrimeTime1(int n) {
clock_t start = clock();
int count = prime1(n);
clock_t end = clock();
double time_taken = double(end - start) / CLOCKS_PER_SEC;
cout << "方法 1:1 到 " << n << " 的素数个数为 " << count << ",用时 " << time_taken << " 秒。" << endl;
}
void PrimeTime2(int n) {
clock_t start = clock();
int count = prime2(n);
clock_t end = clock();
double time_taken = double(end - start) / CLOCKS_PER_SEC;
cout << "方法 2:1 到 " << n << " 的素数个数为 " << count << ",用时 " << time_taken << " 秒。" << endl;
}
int main() {
int n;
cout << "输入 n(n>100000):";
cin >> n;
PrimeTime1(n);
PrimeTime2(n);
return 0;
}
代码思路
一、代码思路
- 函数定义
isPrime1
函数:采用传统方法判断一个整数是否为素数。从 2 开始到该数减 1 进行遍历,如果存在能整除该数的数,则不是素数,否则是素数。时间复杂度为。prime1
函数:利用isPrime1
函数,遍历从 2 到n
的所有整数,统计其中素数的个数。isPrime2
函数:改进的方法判断素数。先处理特殊情况小于 2 的数、2 和 3。然后根据素数的性质,只需要判断到该数的平方根即可,并且只需要考虑 6 的倍数两侧的数(即i
和i + 2
),因为所有大于等于 5 的素数一定可以表示为 6k ± 1 的形式。时间复杂度为。prime2
函数:与prime1
类似,使用isPrime2
函数遍历从 2 到n
的整数,统计素数个数。PrimeTime1
函数:测量prime1
函数的执行时间,并输出素数个数和用时。PrimeTime2
函数:测量prime2
函数的执行时间,并输出素数个数和用时。
main
函数- 提示用户输入一个大于 100000 的整数
n
。 - 分别调用
PrimeTime1
和PrimeTime2
函数,对两种方法进行测试并输出结果。
- 提示用户输入一个大于 100000 的整数
二、原理
-
素数的定义:素数是指一个大于 1 的自然数,除了 1 和它自身外,不能被其他自然数整除。
-
两种判断素数方法的原理:
- 方法 1:传统方法通过遍历所有小于该数的整数来判断是否有能整除它的数。这种方法虽然直观,但效率较低,因为需要进行大量的除法运算。
- 方法 2:改进的方法利用了素数的分布规律。首先处理特殊情况,然后只需要判断到该数的平方根,并且只考虑特定形式的数,减少了不必要的判断,提高了效率。
-
统计素数个数的原理:通过遍历一定范围内的整数,对每个整数使用判断素数的函数进行判断,如果是素数则计数器加一。
-
测量执行时间的原理:使用
clock_t
类型记录程序开始和结束的时间,通过计算时间差并除以CLOCKS_PER_SEC
得到程序的执行时间,以秒为单位。
通过比较两种方法,可以看出在处理较大范围的素数个数计算时,改进后的方法(方法 2)具有更高的效率,因为其时间复杂度更低。
实验题4:求连续整数阶乘的和
题目描述
编写一个程序,对于给定的正整数n,求1!+2!+3!+…+n!。给出一种时间复杂度为O(n)的解法。
运行代码
//实验题4:求连续整数阶乘的和
#include <iostream>
using namespace std;
long long Sum(int n) {
long long sum = 0;
long long fact = 1;
for (int i = 1; i <= n; i++) {
fact *= i;
sum += fact;
}
return sum;
}
int main() {
int n;
cout << "输入正整数 n(3-20):";
cin >> n;
if (n < 3 || n>20)return 0;
long long result = Sum(n);
cout << "1! + 2! + 3! +... + " << n << "! 的结果为:" << result << endl;
return 0;
}
代码思路
一、代码思路
Sum
函数:- 循环结束后,返回
sum
,即从 1 到n
的连续整数阶乘之和。 - 通过循环从 1 到
n
遍历整数。在每次循环中,先将fact
乘以当前的整数i
,得到当前整数的阶乘,然后将这个阶乘累加到sum
中。 - 初始化变量
fact
为 1,用于存储当前整数的阶乘。 - 初始化变量
sum
为 0,用于存储连续整数阶乘的和。
- 循环结束后,返回
main
函数:- 提示用户输入一个正整数
n
,要求在 3 到 20 之间。 - 如果输入的
n
不在这个范围内,程序直接返回 0 退出。 - 调用
Sum
函数计算从 1 到n
的连续整数阶乘之和,并将结果存储在result
中。 - 输出结果,显示从 1 到
n
的连续整数阶乘之和的具体值。
- 提示用户输入一个正整数
二、原理
-
阶乘的定义:一个正整数的阶乘是所有小于及等于该数的正整数的乘积。例如,5 的阶乘是 5! = 5×4×3×2×1 = 120。
-
计算连续整数阶乘之和的原理:
- 通过循环依次计算每个整数的阶乘,并将其累加到总和中。
- 首先,初始化阶乘为 1,因为 1 的阶乘是 1。然后,从 2 开始,每次将当前整数乘以当前的阶乘得到新的阶乘,并将新的阶乘累加到总和中。
- 这样,通过循环可以依次得到 2!、3!、4!…… 直到
n!
,并将它们累加到总和中,最终得到从 1! 到n!
的连续整数阶乘之和。