《英雄编程体验课》第 03 课 | 枚举

零、写在前面

  该章节节选自 《LeetCode零基础指南》,为入门编程的基础内容,主要是教会大家如何学会使用循环语句来写代码。
  当然,如果已经对本套体验课了如指掌,那么可以通过 算法全套课程 联系到我,领取限时全套课程优惠。
  博主所有的课程都是基于 c/c++ 的,java 我不会,但是我一直强调,学习算法和语言无关,算法只是一个思想,只要学好一门语言,就可以学习算法。

一、概念定义

  对于循环来说,C/C++ 语言中总共有两种结构,while 和 for,但是对于刷题来说,其实两者没有本质区别,所以这里我只介绍相对较为简单的 for 语句。

1、语法规则

  for 语句的一般形式为:

for(循环初始化表达式; 循环条件表达式; 循环执行表达式){
    循环体
}

它的运行过程为:
  1)首先,执行 循环初始化表达式
  2)然后,执行 循环条件表达式,如果它的值为真,则执行循环体,否则结束循环;
  3)接着,执行完循环体后再执行 循环执行表达式
  4)重复执行步骤 2)和 3),直到 循环条件表达式 的值为假,则结束循环。

  上面的步骤中,2)和 3)是一次循环,会重复执行,for 语句的主要作用就是不断执行步骤 2)和 3)。执行过程如下图所示:

  细心的读者可能会发现,循环体循环执行表达式 其实是可以合并的,是的,的确是这样。在下面的例子中我会讲到。

2、简单应用

  接下来,我们就来实现一个最简单的循环语句,求 1 + 2 + 3 + . . . + n 1 + 2 + 3 + ... + n 1+2+3+...+n 的值。

int sumNums(int n){             // (1)
    int i;                      // (2)
    int sum = 0;                // (3)
    for(i = 1; i <= n; ++i) {   // (4)
        sum += i;               // (5)
    }
    return sum;                 // (6)
}
  • ( 1 ) (1) (1) 这是一个函数,传参是 n n n,返回值为 1 + 2 + 3 + . . . + n 1 + 2 + 3 + ... + n 1+2+3+...+n 的值;
  • ( 2 ) (2) (2) i i i 作为一个循环变量;
  • ( 3 ) (3) (3) 初始化求和的值 s s s 为 0;
  • ( 4 ) (4) (4) for 语句三部分:循环初始化表达式:i = 1;循环条件表达式:i <= n;循环执行表达式:++i
  • ( 5 ) (5) (5) 循环体:sum += i;,表示将 1、2、3、4、… 累加到 sum上;
  • ( 6 ) (6) (6) 返回求和sum的值;

  以上这段代码,实现了一个数学上的 等差数列 求和。也可以用公式来表示,如下: s u m = ( 1 + n ) n 2 sum = \frac {(1 + n)n} {2} sum=2(1+n)n而利用 for 循环,让我们抛开了数学思维,用程序的角度来实现数学问题。

  注意,for 循环中的那个表达式都可以省略,但它们之间的 分号 必须保留。

3、初始化表达式

1)初始化表达式外置

  我们可以把 初始化表达式 提到 for 循环外面,如下:

    int i = 1;                  // (2)
    int sum = 0;                // (3)
    for(; i <= n; ++i) {        // (4)
        sum += i;               // (5)
    }

  其中注释中的编号对应上文代码中的编号。

2)初始化表达式内置

  我们也可以把需要的初始化信息,都在 初始化表达式 内进行赋值,并且用逗号进行分隔,如下:

    int i, sum;                           
    for(i = 1, sum = 0; i <= n; ++i) {    // (4)
        sum += i;                         // (5)
    }

  具体选择哪种方式,是 代码规范 的问题,不在本文进行讨论,都是可以的。

4、条件表达式

  如果省略 条件表达式,那就代表这个循环是一个 死循环,如下:

    int i;                 // (2)
    int sum = 0;           // (3)
    for(i = 1;; ++i) {     // (4)
        sum += i;          // (5)
    }

  这段代码表示这个循环没有 结束条件,那自然就不会结束了,编码过程中应该尽量避免(当然,某些情况下还是需要的,例如游戏开发中的主循环),或者,函数内部有能够跳出函数的方法,比如 break 关键字。

5、执行表达式

  执行表达式 的作用是 让循环条件 逐渐 不成立,从而跳出循环。
  当然,它也是可以省略的,因为 循环体 本身也是一个 语句块,也可以达到类似功能,如下代码所示:

    int i, sum = 0;           // (2)
    for(i = 1; i <= n;) {     // (3)
        sum += i;             // (4)
        ++i;
    }

  这段代码同样达到了求和的功能,因为 执行表达式 被放入了 循环体,这也正是上文所说的 循环体循环执行表达式 合并的问题。

二、题目分析

1、2 的 幂

  实现一个函数isPowerOfTwo,判断一个 32位整型 n n n 是否是 2 的幂。

bool isPowerOfTwo(int n){
    int i;
    unsigned int k = 1;        // (1)
    if(n <= 0) {
        return false;          // (2)
    }
    if(n == 1) {
        return true;           // (3)
    }
    for(i = 1; i <= 31; ++i) {
        k *= 2;                // (4)
        if(k == n) {
            return true;       // (5)
        }
    }
    return false;
}
  • ( 1 ) (1) (1) 定义一个无符号整型 k k k
  • ( 2 ) (2) (2) 如果 n ≤ 0 n \le 0 n0,则必然不是 2 的幂;
  • ( 3 ) (3) (3) 1 必然是 2 的 0 次幂;
  • ( 4 ) (4) (4) 枚举所有 2 的幂 2 2 2 4 4 4 8 8 8、… 、 2 31 2^{31} 231
  • ( 5 ) (5) (5) 一旦找到一个和 n n n 相等,返回true,则说明它是 2 的幂;
  • ( 6 ) (6) (6) 最后,没有找到的话,返回false表示不是 2 2 2 的幂;

2、3 的幂

  实现一个函数isPowerOfThree,判断一个 32位整型 n n n 是否是 3 的幂。

bool isPowerOfThree(int n){
    int i;
    unsigned int k = 1;
    if(n <= 0) {
        return false;
    }
    if(n == 1) {
        return true;
    }
    for(i = 1; i <= 20; ++i) {  // (1)
        k *= 3;                 // (2)
        if(k == n) {
            return true;
        }
    }
    return false;
}

和 2的幂 那一题相比,只改了两个地方:

  • ( 1 ) (1) (1) 第一个是枚举的上限只需要到 20,因为 3 21 3^{21} 321 应该超过 32位 整型的上限了;
  • ( 2 ) (2) (2) 第二个是每次枚举的是 1 1 1 3 3 3 9 9 9、…、 3 20 3^{20} 320,所以 k k k 不断乘 3。

3、4 的幂

  实现一个函数isPowerOfFour,判断一个 32位整型 n n n 是否是 4 的幂。

bool isPowerOfFour(int n){
    int i;
    unsigned int k = 1;
    if(n <= 0) {
        return false;
    }
    if(n == 1) {
        return true;
    }
    for(i = 1; i <= 15; ++i) {  // (1)
        k *= 4;                 // (2)
        if(k == n) {
            return true;
        }
    }
    return false;
}

和 3的幂 那一题相比,只改了两个地方:

  • ( 1 ) (1) (1) 第一个是枚举的上限只需要到 15,因为 4 16 4^{16} 416 应该超过 32位 整型的上限了;
  • ( 2 ) (2) (2) 第二个是每次枚举的是 1 1 1 4 4 4 16 16 16、…、 4 15 4^{15} 415,所以 k k k 不断乘 4。

4、n 的第 k 个因子

  给你两个正整数 n n n k k k 。如果正整数 i i i 满足 n % i == 0,那么我们就说正整数 i i i 是整数 n n n 的因子。考虑整数 n n n 的所有因子,将它们 升序排列 。请你返回第 k k k 个因子。如果 n n n 的因子数少于 k k k ,请你返回 − 1 -1 1

int kthFactor(int n, int k){
    int i;
    int cnt = 0;                // (1)
    for(i = 1; i <= n; ++i) {   // (2)
        if(n % i == 0) {        // (3)
            ++cnt;
            if(cnt == k) {
                return i;       // (4)
            }
        }
    }
    return -1;                  // (5)
}
  • ( 1 ) (1) (1) 定义一个计数器cnt,初始化为 0;
  • ( 2 ) (2) (2) 枚举所有范围在 [ 1 , n ] [1, n] [1,n] 数,因为只有这些数才可能是 n n n 的因子;
  • ( 3 ) (3) (3) 一旦满足 n % i == 0,则计数器加一;
  • ( 4 ) (4) (4) 当计数器等于 k k k,则代表找到了 第 k k k 个因子,直接返回即可;
  • ( 5 ) (5) (5) 如果一直没有找到,则表示不存在 第 k k k 个因子,返回 − 1 -1 1

5、有效的完全平方数

  给定一个 正整数 x x x ,编写一个函数,如果 x x x 是一个完全平方数,则返回true,否则返回 false

int isPerfectSquare(int x){
    int i;
    long long p;
    for(i = 1; ; ++i) {      // (1)
        p = (long long)i*i;  // (2)
        if(p == x) {
            return true;     // (3)
        } 
        if(p > x) {
            return false;    // (4)
        }
    }
    return false;            // (5)
}
  • ( 1 ) (1) (1) 定义一个死循环,枚举所有数;
  • ( 2 ) (2) (2) 枚举所有数的平方,并且注意用 long long 避免 32整型溢出;
  • ( 3 ) (3) (3) 如果发现某个数的平方正好等于 x x x,则直接返回true
  • ( 4 ) (4) (4) 如果发现枚举的数已经大于 x x x,无须再往下枚举,直接返回false
  • ( 5 ) (5) (5) 这段代码是保底,理论上不可能跑到;

三、推荐学习

🌌《算法零基础100讲》🌌

(第1讲) 幂和对数

四、课后习题

  坚持!加油!你可以的!

序号题目链接难度必做
1求1+2+…+n★☆☆☆☆1
22 的幂★☆☆☆☆1
33 的幂★☆☆☆☆1
44 的幂★☆☆☆☆1
5n 的第 k 个因子★☆☆☆☆0
6完全平方数★☆☆☆☆0
  • 12
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

英雄哪里出来

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值