目录

前言

一、引例 -- 菱形打印

1. 题干要求

2. 如何阅读循环结构?

二、“在盒子里过家家”的理解思路

1. 什么叫“在盒子里过家家”?

一个小例子 -- 到底打印多少个"*"?

2. 如何运用该思路详解引例?

代码实现

三、思路应用 -- 冒泡排序

四、思路应用 -- 菱形打印的代码优化

五、总结


本文采用C++的代码风格完成代码实现。


前言

对于初学者而言,应用循环嵌套来解决问题,是一个说起来容易,但实现起来其实蛮棘手的一个事情。有的同学一看 for循环嵌套就懵了,看着层叠的代码直呼头大,掰着手指头数循环次数;看着教材、辅导书和百科上解释的只言片语似懂非懂;或是能勉强看懂,但一到相似的编程题目要自己写了,又东凑西凑写不出来,好不容易憋出两个for,一运行程序秒挂。

理解不了循环嵌套的用意,不会判断什么时候使用循环嵌套,什么东西写在循环内还是循环外搞不清楚,出了bug不知道从哪里入手开始改……这些都是初学者常常遇到的问题,其实是非常正常的。事实上这也是我刚开始接触编程语言时,非常非常困扰我的问题(因为它说来简单,却总是因为一些小问题出一堆错误,get不到题目要的那个点)。

经过更深入的学习和自然的刷题,我自己对这个问题作出了一些总结,得出的这个结论概括来说,用七个字来描述循环嵌套:在盒子里过家家。

本文中,我将详细介绍该思想,以菱形打印为引例加以说明,希望能把我的这个经验讲述给大家,对大家学习编程语言有所帮助,对二维数组的理解也相辅相成。

不需要引例的朋友可以直接跳转到讲解部分。

以下即为正文内容。


一、引例 -- 菱形打印

1. 题干要求

打印实心菱形,菱形的大小由输入决定。

效果展示

当输入为9(或10,菱形只能是奇数行)时,打印9行菱形;

当输入为5时,打印5行菱形;



这是在循环结构部分的一道经典编程题。它的核心就是恰当地使用循环嵌套。对于入门编程有些时日的朋友,这自然是非常基础。但以我自身经历来说,第一次写这道题,我用了将近一个半小时,打印出了各种奇奇怪怪的歪瓜裂枣……最后甚至开凑(当然,凑是没用的)。

在讲循环嵌套之前,我先介绍一种阅读循环的技巧,在讲解循环时,大家能理解地更加清晰。

2. 如何阅读循环结构?

在网上或各种编程辅导书中,我们能看到各种各样的代码展示与解析:

网上截取的解法展示 1

网上截取的解法展示 2

上面两张图中的菱形解法是从网络上截取的,相信很多人都看过。

这些代码都是正确的,确实可以实现菱形的打印。但我认为,如果是这样的答案——那么它们仅能算是合格的答案,并不能算合格的讲解。这样的解答贴出“第一步xxx”“第二步xxxx”,我们看完这些答案后,能顺着其中的注释从头到尾看一遍,也许能明白每一步是干什么的,知道这代码是可行的,但这对于“举一反三”是远远不够的。

因为我们很难从其中形成对循环结构和“为什么这样就能打印出菱形”原理的系统的认知。就好像高中读文言文,将每一句话的释义抄得很细,也明白它大体讲的是什么,可是却忽略了作者安排行文结构的良苦用心。

我们要理解为什么代码会是这样的——在读循环结构的代码时,我们就应当调整一下习惯。通俗的来说,在读带有 for 、while、switch、函数等带有 { } 的代码块时,第一眼不要按照习惯从上向下读,而最好先找到后括号 } ,确认代码块在哪里结束,粗略地浏览整体结构。

这样做的目的,是为了明确(可以手动用笔划,熟练后也可以之间在心里画)层次。像这样:

一眼看上去就知道这个循环里面干了什么

非常清晰,也能提高阅读代码的效率。

(如果一时还没有找到好的阅读方法,推荐尝试。

在介绍完如何阅读循环结构代码后,接下来咱们就来探讨“在盒子里过家家”到底是怎样一种思路。

By the way, 我们以常规的代码来解析菱形打印这道引例。而在文末也将附上优化版本的代码。优化版本的代码不作过多解析,感兴趣的朋友们可以copy下来试一试,加深理解。


二、“在盒子里过家家”的理解思路

1. 什么叫“在盒子里过家家”?

卖关子到此为止。“在盒子里过家家”自然是比喻的说法。这个思路提供的,是思考的切入点:以盒子为单位进行思考。

假设眼前有两层 for 循环,外层的for循环每循环一次,就创造出一个盒子(就像流水线上的快递箱),盒子内部就是外层for循环的内部。

盒子内部要装很多东西,干它自己的事情,这就是盒子里面的“过家家”。

内层for循环就是盒子里面普通的一员。它根据需要摆放着相应的位置。

我们要有意识地弱化“循环嵌套”这个概念,而把使用循环作为和使用输入输出函数一样自然的事情。不必紧张于,某个题目要考所谓的“循环嵌套”而我不会之类的事情。

在循环里面再写一个循环,就和在盒子里面随手放一个东西一样自然。

记住,以盒子为单位进行思考。类似于二维数组,不过我们也并不强调“列”的概念。行是最重要的,因为它恰代表着外层for循环。列的概念却并不在所有循环嵌套中都具象。

有一些同学搞不清外循环和内循环的关系和一些循环次数的问题。其实不要刻意地想要将内外循环联系起来:内循环没什么特别的关系,它只不过是外循环里面的一个代码块,相当于盒子里面一个普通的元素。

当外层for循环写好后,往里面填代码,就相当于是向盒子里面塞东西。进入盒子之后,视野变小,就不要想着盒子了(进入外层for循环之后,没必要再纠结外层for循环了,就当它没有,按从上到下的顺序阅读/填写内部的代码即可)

可以尝试一下理解“视野变小”。

这样还是很抽象,我们直接用代码来理解:

for(int i = 0; i < 5; i++){cout << "hello!box!" << " ";for(int j = 0; j < 3; j++){cout << "good" << " ";}cout << "end" << endl;}

盒子就相当于每一行代码。

盒子 ---- 行

每个盒子里面有什么内容 ---- 每行要做什么事

每行要做什么事啊?每行会先打印一个"hello!box!  ",再打印一个3个"good  ",再打印一个''end"

这个盒子结束了 ---- 这行结束了(该换下一行了)

下一行要做什么事啊?会先打印一个"hello!box!  ",再打印一个3个"good  ",再打印一个''end"

……

一共有5个盒子 ---- 一共有5行

每个盒子都一样 ---- 每一行的内容都一样


一个小例子 -- 到底打印多少个"*"?

看一看下面的代码:

int i = 0;
int j = 0;//1-这里打印多少个*?
for(i=0;i<10;i++){for(j=0;j<10;j++){cout << "*"; }
}//---------------------------------------------------int i = 0;
int j = 0;//这里打印多少个*?
for(  ;i<10;i++){for(  ;j<10;j++){cout << "*"; }
}

第一题打印100个"*",因为每个盒子里面都打印10个"*",而一共有10个盒子(或者直接理解为二维数组,轻轻松松)。

第二题打印10个"*" ,因为虽然一共还是有10个盒子,但只有第一个盒子里面,打印了10个"*",第一个盒子之后,j 的值始终是10,剩下盒子里面的小for循环就都进不去啦!


2. 如何运用该思路详解引例?

来看题。

我们用“行”来代替上述“盒子”的概念,走一走这道题。以盒子为思考单位,在本题中,是以“行”为思考单位。

我们知道,本题首先要找出规律:每一行打印几个空格几个"*"

规律

我们以行为思考单位,思考每一行需要做什么。这些东西,到时候都是直接填充的外层for循环中的东西。

如图示代码,我们先将其分层,确定结构。重点看循环嵌套部分。它的构造思路:

1. 确定行数,即外层for循环的参数 i

2. 确定每一行里面要做什么(思路:先……再……然后……最后)

  • 先(n+1-i)个空格,要打印这么多,自然而然地使用for循环
  • 再打印"*",因为是等到空格打印完了才打印"*",所以关于"*"的部分要排在空格后面,讲究先来后到
  • 最后换行,因为是每一行行末,图案打印完了才要换行,所以这句话排在最后。(有的人会误写到外层for循环外面……以行为单位思考,行里不就没有换行了嘛?)

代码实现

#include<iostream>
using namespace std;
int main()
{// i是行数//  n=5的时候
//  第1行 2个空格  1个*
//  第2行 1个空格  3个*
//  第3行 0个空格  5个*
//
//  第i行 n+1-i个空格  2*i-1个*int input_line;int i,j,k; //遍历用的变量cout << "please input a number:>  "<< endl << endl;cin >> input_line;//    n是行数的一半 n+1是中间行int n = (input_line-1)/2; //   输出上半段for(i=1; i<=n+1; i++){//一行里面 先输出n+1-i个空格 for(j=1;j<=n+1-i;j++){cout << " ";}//再输出2*i-1个*for(k=1;k<=2*i-1;k++){cout << "*";}//再换行cout << endl;}// 输出下半段-- 和上面完全一样,就是行数要少一行for(i=n;i>=1;i--){//先输出n+1-i 个空格for(j=1;j<=n+1-i;j++){cout << " ";}//再输出2*i-1个*for(k=1;k<=2*i-1;k++){cout << "*";}cout << endl;}return 0;
}

三、思路应用 -- 冒泡排序

找了两张图,看哪张都一样。有人觉得冒泡排序复杂,但用盒子思路,其实很好理解。我们以这两张图来讲解冒泡排序的盒子思路。

自己画的示意图

网上截取的示意图

以盒子为思考单位——这里的盒子——最大最宏观的循环,可以从图中一眼看出:冒泡的趟数。

每一趟里面,具体干了什么呢? 交换逆序元素位置。

所以每一趟要完成:比较相邻两个数的大小并换好位子。

还不够具体。具体怎么比?

1. 比较第1个数和第2个数大小,判断要不要换位子(假设从小到大排),要是原来后一个数比前一个数小(逆序了) ---> 要换位子

2. 比较第2个数和第3个数大小,判断要不要换位子

3. 比较第3个数和第4个数大小,判断要不要换位子

……

第n-1步: 比较第(n-1)个数和第 n 个数大小,判断要不要换位子

好像是循环欸,那就写成循环吧!i 已经用过了,那就用 j 吧!注意,每一趟并不是完全相同的。毕竟前面排好序的数,嫌累的话后面就没必要再排一遍了。

加个 for(j = 0; j < size - 1 - i; j++),是很自然的事情。本题中,外层 i 便是排序的趟数,里面的 j 则是每趟里面要两两交换的次数。代码如下:

 //升序排序for(i=0;i<ARR_SIZE-1;i++){    //每一趟要干什么?for(j=0;j<ARR_SIZE-1-i;j++){    //每次交换要干什么?int tmp;if(a[j]>a[j+1]){tmp=a[j];a[j]=a[j+1];a[j+1]=tmp;}}}

当然,这不是完整的冒泡代码。因为它还能优化:如果中途某一趟发现这些数刚好已经有序了,那不就提前下班不用再干活了吗?因而我们加一个每趟开始对这趟数是否有序的判断:

 //升序排序int xch;for(i=0;i<ARR_SIZE-1;i++){xch = 0;for(j=0;j<ARR_SIZE-1-i;j++){int tmp;if(a[j]>a[j+1]){tmp=a[j];a[j]=a[j+1];a[j+1]=tmp;xch = 1;}}if(!xch){break;}}

就是这样了,利用盒子思路自然而然地思考和运用双层循环。


四、思路应用 -- 菱形打印的代码优化

代码如下,该代码是上面常规代码的优化版本。在此留给感兴趣的朋友们思考研究,不作过多解释了。

#include<iostream>
using namespace std;int main()
{   int i,n,j;cout<<"请输入一个数字:"<<endl;cin>>n;for(i=-n;i<=n;i++){for(j=1;j<=abs(i);j++)cout<<" ";for(j=1;j<=n-2*abs(i);j++)cout<<"*";cout<<endl;}return 0;
}

当然,菱形还可以是空心的,同样的思维模式,改动一下实心菱形代码即可:

空心菱形

#include <iostream>
using namespace std;
int main()
{// i是行数//  n是中间行int i,j,n,input;cout << "please input a number:>  "<< endl << endl;cin>>input;n = input/2;//第一行 n个空格 1个*for(i=1;i<=n;i++){cout<<" ";}cout<<"*"<<endl;//2到n行for(i=1; i<=n; i++){//一行里面先输出n-i个空格for(j=1; j<=n-i; j++){cout<<" ";}//然后输出1个* for(j=1; j<=2; j++){cout<<"*";for(j=1; j<=2*i-1; j++)//*+空格+*的长度{cout<<" ";}cout<<"*";}cout<<endl;}//下半部分for(i=n-1; i>=1; i--){for(j=1; j<=n-i; j++){cout<<" ";}for(j=1; j<=2; j++){cout<<"*";for(j=1; j<=2*i-1; j++){cout<<" ";}cout<<"*";}cout<<endl;}//最后一行 和第一行一样for(i=1;i<=n;i++)cout<<" ";cout<<"*"<<endl;return 0;
}

五、总结

  1. 有意识地弱化“双层循环”这个概念——以盒子为单位思考问题!不要过于紧张所谓几个循环嵌套。对二维数组有一定的了解更好,它们的理解和学习可以是相辅相成的。
  2. 就像输入输出一样,自然而然用到了就写,而不要一上来就刻意地想:噢!这题我要用双层循环了,怎么凑才好呢?事实上不要对任何东西刻意,用心理解而不要刻意去背。
  3. 分析不同题目的“盒子”是什么,尝试迁移。
  4. 多做题找感觉永不过时!
  5. 码字不易,感谢支持!

循环嵌套思路详解 | 一个“在盒子里过家家”的算法 -- 以冒泡排序与打印菱形为例相关推荐

  1. python循环嵌套流程图,详解Python 循环嵌套

    Python 语言允许在一个循环体里面嵌入另一个循环. Python for 循环嵌套语法: for iterating_var in sequence: for iterating_var in s ...

  2. 实体类多层嵌套 遍历_JS获取元素多层嵌套思路详解

    如果一段html嵌套过多,在js中获取还是比较麻烦的,我写了几套方案,大家可以参考参考,如果你有好的方法,也分享出来,让我们瞧瞧. HTML: 元素多层嵌套,JS获取问题 span span 1 2 ...

  3. for的循环嵌套 运算详解

    如图 运算过程如下: 一开始i=1,执行里面的for语句,j=1,然后j++,一直加到j=4时,break跳出里面的for循环,所以前面几行结果i=1  j=1                 i=1 ...

  4. python读取json格式文件大量数据,以及python字典和列表嵌套用法详解

    1.Python读取JSON报错:JSONDecodeError:Extra data:line 2 column 1 错误原因: JSON数据中数据存在多行,在读取数据时,不能够单单用open(), ...

  5. python当型循环_对python while循环和双重循环的实例详解

    废话不多说,直接上代码吧! #python中,while语句用于循环执行程序,即在某个条件下,循环执行某段程序,以处理需要重复处理的相同任务. #while是"当型"循环结构. i ...

  6. python逢7跳过_python实现逢七拍腿小游戏的思路详解

    逢七拍腿游戏 几个小朋友在一起玩逢七拍腿的游戏,从1开始数数,当数到7的倍数或者尾号是7时,拍一下腿.现在从1数到99,假设每个人都没有错,计算一下共要拍腿几次? 第一种实现思路:通过在for循环语句 ...

  7. java excel中重复数据 事务处理_Java导出excel时合并同一列中相同内容的行思路详解...

    一.有时候导出Excel时需要按类别导出,一大类下好几个小类,小类下又有好几个小小类,就像下图: 要实现这个也不难, 思路如下:按照大类来循环,如上就是按照张江校区.徐汇校区.临港校区三个大类循环,然 ...

  8. 2021年美赛A题思路详解

    2021年数模美赛A题思路详解 题目分析 思路详解 由于和队友思路不一致,导致最后我的思路只算了前两问,而后几问用了我认为离题的PCA(主成分分析)的方法,我的建模思路没有得到完全实现,总体情况很不满 ...

  9. python拍七游戏代码_python实现逢七拍腿小游戏的思路详解

    逢七拍腿游戏 几个小朋友在一起玩逢七拍腿的游戏,从1开始数数,当数到7的倍数或者尾号是7时,拍一下腿.现在从1数到99,假设每个人都没有错,计算一下共要拍腿几次? 第一种实现思路:通过在for循环语句 ...

最新文章

  1. 手把手教你EMD算法原理与Python实现(更新)
  2. [转]NLog学习笔记二:深入学习
  3. 推翻Hinton NeurIPS论文结论!审稿人评价:该文章在标签平滑和知识蒸馏的关系上取得了重大突破!...
  4. hadoop-0.20.2安装配置
  5. 官方野生蝇群流口水分数
  6. php 安装mysql扩展注意事项
  7. 1110 Complete Binary Tree (25 分)(搜索)
  8. java调用vissim的com接口_VISSIM 高级应用COM接口开发初级入门
  9. UML课程大作业-网上书店系统
  10. Love Deterrence【MMD动作+镜头下载】
  11. lan口配置 petalinux_【Xilinx-Petalinux学习】-02-建立PetaLinux工程
  12. 做scratch游戏心得1
  13. QQ玩一玩广告与音效使用总结
  14. 牡丹-洛阳牡丹:洛阳牡丹
  15. [高项]假设情景分析VS假设分析
  16. linux 中压缩文件夹命令行,Linux下压缩文件夹命令使用
  17. Unity做360度的全景照片
  18. 舌尖上的职场(三)我来买单!(转)
  19. kali安装中文输入法(搜狗输入法)
  20. 计算机导论的平时分多少,学霸养成 | 大一期末考试经验第二弹,千万别错过!...

热门文章

  1. 利用SecureCRT工具在Windows与Linux传输文件命令
  2. CV_8UC1,CV_8UC2,CV_8UC3, CV_32FC3, CV_64FC3等区别
  3. 勤于奋抖音推文知乎小说变现第四课:如何找素材
  4. 前端画圆弧html弧线的像素,[js高手之路] html5 canvas系列教程 - arc绘制曲线图形(曲线,弧线,圆形)...
  5. mfc小工具开发之定时闹钟之---时间获取和音频播放
  6. 云手机的虚拟手机服务器是什么意思,云虚拟手机服务器挂机
  7. 计算机领域各大顶会顶刊集合梳理
  8. 简单快速PS制作绚丽光斑散景效果
  9. (转)有铅焊锡与无铅焊锡的区别
  10. asp.net速查手册呵呵