不同的程序员会如何计算1+2+...+n

目录

方法一:普通的循环语句法(初学者的玩法)

方法二:使用递归(进阶学者的玩法)

方法三:使用动态规划(更进一步的玩法)

方法三:直接套公式法(懒人必选,面试新手必备)

方法四:库函数直接调用法(对库函数或者力扣比较了解的玩法)

accumulate用法

方法五:类对象构造函数n次调用法(超越Al的方法,计算机大佬炫技必备,另一种不使用循环的巧解)


大部分我们科班的学生都是从大一开始接触编程的吧,当你们开始学循环章节的时候,都或多或少的会在课堂上看到你们老师将从1加到n的算法。今天我们就再来盘点一下从1加到n的主流和非主流的算法。内容有点长,但是干货满满!!!

方法一:普通的循环语句法(初学者的玩法)

无论是使用for循环,while循环,还是对于初学者较为不熟悉也不常用的do while循环,效果都差不多,其本质都是循环,只不过do while循环是先执行某条语句再进行循环,其他的是先进行循环

很简单,但是很常见,也比较易懂。

int forcir(int m)
{
    int sum = 0;
    for (int i = 1; i <= m; i++)
    {
        sum = sum + i;
    }
    return sum;
}

方法二:使用递归(进阶学者的玩法)

由于我们发现,当要计算从1+...+i时每次相当于先计算了1+...+i-1,再加上最后的i,这样每层就需要先得出上一层的结果再加上这一层的新加的数i,这样一直倒推只要得到第一层的值就可以反推出最后一层的值了,这就满足递归的条件和结束条件了。

int recur(int m)
{
    if (m == 1)
    {
        return 1;
    }
    return recur(m - 1) + m;
}

上面这种使用递归的方法虽然时间复杂度不会升高或者降低多少,但是没有使用循环,这个是关键点,当然如果有的人实在是看不懂的话,也可以使用栈这个数据结构进行对递归的正向调用,由于无论是使用栈还是递归都差不多,而且使用栈还得使用for循环,所有就不写了!!!

方法三:使用动态规划(更进一步的人的玩法)

能想到动态规划,能想到动态规划说明你不仅对算法有浓厚的兴趣还对动态规划有进行总结的。

因为动态规划有一种类型就是递归型的。

由于我们刚刚在递归那里分析过了,你要计算当前i层的从1+...+i的值,就相当于要先计算i- 1层的值再加上i,这样如果我们定义dp[i] 的值为第i层的1+...+i的加和,那么第i层的值的状态就等于前一层的再加上i。这样状态表达式就出来了。

int dynamic(int m)
{
    vector<int> dp(m);
    dp[0] = 1;
    for (int i = 1; i < m; i++)
    {
        dp[i] = dp[i - 1] + i + 1;
    }
    return dp[m - 1];
}

因为这时的时间复杂度还是O(n),所以如果你想的话,可以使用滚动数组的逻辑进行优化,使其达到O(1)。

方法三:直接套公式法(懒人必选,新手面试必备)

不要小看这种方法,因为虽然看着简单但是容易让算法题刷得太多的人忘记,如果就这个1+...+n的题我不让你使用循环,且空间复杂度,时间复杂度都要最小的话,递归算法满足了不使用循环,但是时间复杂度和空间复杂度可能会因为递归的层数过多而很大,所有当n很大时递归就无法计算了,这时我们就应该想到直接使用高斯求和公式!!!

这个方法是最优化的解决方案,适用于任意规模的n。

int math(int m)
{
    int math = (1 + m) * m / 2;
    return math;
}

方法四:库函数直接调用法(对库函数或者力扣比较了解的人的玩法)

我们std库里面的accumulate函数可以提供对容器成员的多个数的累加。

我们关注一下那个sum版本就可以了,因为sum版本就是简单的加和版本,后面那个custom版本是自定义操作符版本,就是比上面那个多了传了一个仿函数或者lambda,使其自定义累加的元素进行自定义的操作。

accumulate用法

注意:很多人会以为这个累加的初始值(init)是累加的起始位置的值,这是不对的,这个值是全部需要累加的数的起始值也就是说,累加总数是从init开始的,然后再是你给定范围的容器的第一个数vector[first],实际上sum = init + vector[first] + vector[second] +...+ vector[last]

那么就是说,只要我们将1,2....n的值存在一个vector中,就可以使用vector迭代器进行控制范围从而使用accumulate进行加和。为什么是对力扣比较了解的玩法呢?因为力扣的函数一般都会传STL容器的对象,那么官方解答遇到需要加和时就经常会使用这个函数,同样遇到要在容器里面求出很多元素中的最大值和最小值时也会偏向于使用max_element和min_element

int useaccumulate(int m)
{
    vector<int> use;
    for (int i = 1; i <= m; i++)
    {
        use.push_back(i);
    }
    int sum = accumulate(use.begin(), use.end(), 0);
    return sum;
}

方法五:类对象构造函数n次调用法(超越Al的方法,计算机大佬炫技必备,另一种不使用循环的巧解)

有同学可能会说,递归我看来看去就是看不懂,库函数没有了解那么多,高斯求和突然想不起来,leetcode刷得也不够多,那还有没有不使用循环解出这个题的方法呢?当然还有一种比较阴间的方法:如下

class calculate
{
public:
    calculate()
    {
        sum += a;
        a += 1;
    }
    static int sumget()//为了取到++之后的总和sum这个私有成员,普通成员函数虽然可以取到但是由于不在类里所以不能使用类对象直接返回
    {                  //只能将其弄成静态的
        return sum;
    }
private:
    static int a;//使用静态的成员变量是为了使得每一个对象的成员对象都一样,使得构造函数进行操作的对象都一样,便于++
    static int sum;
};

int calculate::a = 1;//两个静态变量只能在类的外面进行初始化,这是语法规定
int calculate::sum = 0;
int main()
{
    int n = 0;
    cin >> n;
    //calculate arr[n];//由于有些编译器不支持这种变长数组的写法,所有我们一般不用
    vector<calculate> arr(n);
    cout << calculate::sumget() << endl;
    return 0;
}

这个方法巧妙之处就是巧借每一个对象的创建都需要调用构造函数,然后将加和的过程放在构造函数里面来完成,然后再将加和的总值返回。

当然这种通过类对象实现的方法虽然可以最大程度的体现你的技术水平很不错,但是由于从1加到n就意味着你需要向数组开n个空间,比较浪费空间。面对这么简单的问题,我们日常生活中肯定不会优先考虑这种方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值