数据结构实验1

实验题1:求1到n的连续整数和

题目描述

编写一个程序,对于给定的正整数n,求1+2+...十n,采用逐个累加与(n+1)/2(高斯法)两种解法。对于相同的n,给出这两种解法的求和结果和求解时间,并用相关数据进行测试。

运行代码

cpp 复制代码
//实验题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;
}

代码思路

  1. 头文件和命名空间

    • 包含了必要的输入输出流头文件 <iostream>、时间相关头文件 <time.h>、标准输入输出头文件 <stdio.h> 和数学库头文件 <math.h>
    • 使用 using namespace std 引入标准命名空间。
    • 定义宏 lllong long,方便后续使用长整型数据类型。
  2. 函数定义

    • add1 函数:使用循环遍历从 1 到 n 的整数,逐个累加求和。
    • AddTime1 函数:测量 add1 函数的执行时间,并输出结果和用时。
    • add2 函数:利用数学公式 n*(n+1)/2 直接计算从 1 到 n 的整数之和。
    • AddTime2 函数:测量 add2 函数的执行时间,并输出结果和用时。
  3. main 函数

    • 从用户输入获取整数 n,要求 n 大于 1000000,否则程序退出。
    • 分别调用 AddTime1AddTime2 函数,对两种方法进行测试。
  4. 方法一的原理:循环遍历从 1 到 n 的每个整数,逐个累加到变量 sum中,最终得到从 1到 n 的整数之和。这种方法直观易懂,但当 n 较大时,循环执行次数较多,可能会比较耗时。

  5. 方法二的原理:利用数学公式 1 + 2 + 3 +... + n = n*(n+1)/2 直接计算从 1 到 n 的整数之和。这个公式可以通过数学归纳法证明。这种方法避免了循环遍历,计算效率更高,尤其是对于较大的 n。

通过测量两种方法的执行时间,可以看出当 n 较大时,方法二通常比方法一执行速度更快,因为方法二避免了大量的循环迭代操作,直接利用数学公式进行计算。

实验题2 : 常见算法时间函数的增长趋势分析

题目描述

运行代码

cpp 复制代码
//实验题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;
}

代码思路

一、代码思路

  1. 函数定义
    • log2函数:通过换底公式logₐb = logₑb / logₑa,这里使用以 10 为底的对数函数log10计算出以 2 为底的对数。
    • factorial函数:使用递归的方式计算给定整数n的阶乘。当n为 0 或 1 时,阶乘为 1;否则,n的阶乘等于n乘以n - 1的阶乘。
  2. main函数
    • 首先输出表头,展示不同算法时间函数的名称。
    • 使用循环从 1 到 10 遍历整数n
    • 在循环中,分别计算并输出n、以 2 为底n的对数、n的平方根、nn的平方、n的立方、2 的n次方、n的阶乘的值。

二、原理

  1. log2(n):对数函数的增长非常缓慢。随着n的增大,对数函数的值增长速度远远慢于线性增长。
  2. √n:平方根函数的增长速度比线性增长慢,但比对数函数快。
  3. n:代表线性增长,即随着n的增加,函数值以相同的比例增加。
  4. n^2:二次函数的增长速度比线性增长快得多。当n增大时,二次函数的值增长迅速。
  5. n^3:三次函数的增长速度比二次函数更快。
  6. 2^n:指数函数的增长速度非常快,远远超过其他函数。
  7. n!:阶乘函数的增长速度是所有这些函数中最快的。随着n的增大,阶乘函数的值呈爆炸式增长。

通过输出这些不同函数在不同n值下的结果,可以直观地观察到它们的增长趋势差异,对于分析算法的时间复杂度非常有帮助。例如,在选择算法时,如果一个算法的时间复杂度是指数级的(如2^nn!),那么对于较大的输入规模,该算法可能会非常耗时,而如果算法的时间复杂度是线性的(如n)或对数级的(如log2(n)),则通常会更加高效。

实验题3:求 数的个数

题目描述

编写一个程序,求1~n的素数个数。给出两种解法,对于相同的n,给出这两种解法的结果和求解时间,并用相关数据进行测试。

运行代码

cpp 复制代码
//实验题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;
}

代码思路

一、代码思路

  1. 函数定义
    • isPrime1函数:采用传统方法判断一个整数是否为素数。从 2 开始到该数减 1 进行遍历,如果存在能整除该数的数,则不是素数,否则是素数。时间复杂度为。
    • prime1函数:利用isPrime1函数,遍历从 2 到n的所有整数,统计其中素数的个数。
    • isPrime2函数:改进的方法判断素数。先处理特殊情况小于 2 的数、2 和 3。然后根据素数的性质,只需要判断到该数的平方根即可,并且只需要考虑 6 的倍数两侧的数(即ii + 2),因为所有大于等于 5 的素数一定可以表示为 6k ± 1 的形式。时间复杂度为。
    • prime2函数:与prime1类似,使用isPrime2函数遍历从 2 到n的整数,统计素数个数。
    • PrimeTime1函数:测量prime1函数的执行时间,并输出素数个数和用时。
    • PrimeTime2函数:测量prime2函数的执行时间,并输出素数个数和用时。
  2. main函数
    • 提示用户输入一个大于 100000 的整数n
    • 分别调用PrimeTime1PrimeTime2函数,对两种方法进行测试并输出结果。

二、原理

  1. 素数的定义:素数是指一个大于 1 的自然数,除了 1 和它自身外,不能被其他自然数整除。

  2. 两种判断素数方法的原理:

    • 方法 1:传统方法通过遍历所有小于该数的整数来判断是否有能整除它的数。这种方法虽然直观,但效率较低,因为需要进行大量的除法运算。
    • 方法 2:改进的方法利用了素数的分布规律。首先处理特殊情况,然后只需要判断到该数的平方根,并且只考虑特定形式的数,减少了不必要的判断,提高了效率。
  3. 统计素数个数的原理:通过遍历一定范围内的整数,对每个整数使用判断素数的函数进行判断,如果是素数则计数器加一。

  4. 测量执行时间的原理:使用clock_t类型记录程序开始和结束的时间,通过计算时间差并除以CLOCKS_PER_SEC得到程序的执行时间,以秒为单位。

通过比较两种方法,可以看出在处理较大范围的素数个数计算时,改进后的方法(方法 2)具有更高的效率,因为其时间复杂度更低。

实验题4:求连续整数阶乘的和

题目描述

编写一个程序,对于给定的正整数n,求1!+2!+3!+...+n!。给出一种时间复杂度为O(n)的解法。

运行代码

cpp 复制代码
//实验题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;
}                

代码思路

一、代码思路

  1. Sum函数:
    • 循环结束后,返回sum,即从 1 到n的连续整数阶乘之和。
    • 通过循环从 1 到n遍历整数。在每次循环中,先将fact乘以当前的整数i,得到当前整数的阶乘,然后将这个阶乘累加到sum中。
    • 初始化变量fact为 1,用于存储当前整数的阶乘。
    • 初始化变量sum为 0,用于存储连续整数阶乘的和。
  2. main函数:
    • 提示用户输入一个正整数n,要求在 3 到 20 之间。
    • 如果输入的n不在这个范围内,程序直接返回 0 退出。
    • 调用Sum函数计算从 1 到n的连续整数阶乘之和,并将结果存储在result中。
    • 输出结果,显示从 1 到n的连续整数阶乘之和的具体值。

二、原理

  1. 阶乘的定义:一个正整数的阶乘是所有小于及等于该数的正整数的乘积。例如,5 的阶乘是 5! = 5×4×3×2×1 = 120。

  2. 计算连续整数阶乘之和的原理:

    • 通过循环依次计算每个整数的阶乘,并将其累加到总和中。
    • 首先,初始化阶乘为 1,因为 1 的阶乘是 1。然后,从 2 开始,每次将当前整数乘以当前的阶乘得到新的阶乘,并将新的阶乘累加到总和中。
    • 这样,通过循环可以依次得到 2!、3!、4!...... 直到n!,并将它们累加到总和中,最终得到从 1! 到n!的连续整数阶乘之和。
相关推荐
yuanbenshidiaos1 分钟前
c++------------------函数
开发语言·c++
yuanbenshidiaos5 分钟前
C++----------函数的调用机制
java·c++·算法
唐叔在学习9 分钟前
【唐叔学算法】第21天:超越比较-计数排序、桶排序与基数排序的Java实践及性能剖析
数据结构·算法·排序算法
ALISHENGYA29 分钟前
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之分支结构(switch语句)
数据结构·算法
tianmu_sama29 分钟前
[Effective C++]条款38-39 复合和private继承
开发语言·c++
chengooooooo31 分钟前
代码随想录训练营第二十七天| 贪心理论基础 455.分发饼干 376. 摆动序列 53. 最大子序和
算法·leetcode·职场和发展
jackiendsc38 分钟前
Java的垃圾回收机制介绍、工作原理、算法及分析调优
java·开发语言·算法
羚羊角uou44 分钟前
【C++】优先级队列以及仿函数
开发语言·c++
姚先生971 小时前
LeetCode 54. 螺旋矩阵 (C++实现)
c++·leetcode·矩阵
FeboReigns1 小时前
C++简明教程(文章要求学过一点C语言)(1)
c语言·开发语言·c++