(2022/01/13更新 新版解法)
注:本人CS系大学生。C之前有接触过但基本忘得差不多了,目前在重新学习C。
今天看到个题目,说是用C的for循环语句打印一个图示的实心菱形:
对别人来说很简单,但对于我这个刚刚认识完循环语句的新手来说很难。光是想就想了很久很久,终于想出来了。写这篇博客一是将自己的思路存档供日后查看,二是把我的思路分享给大家,希望对同样是学习路上的大家有所帮助。完整源码在文末。
题目要求打印的效果是菱形,但实际上我们可以构造一个7*7的正方形。只不过菱形的组成部分用星号来表示,而空白部分则用表示。这里以一个7*7的菱形为例:
空白效果:
实际效果:
那第一步思路就清晰地出来了,使用两个for语句来生成空白的正方形:
#include<stdio.h>
int main() {
int coloum = 7; // 定义菱形的宽(高)度
// i代表行, j代表列
for (int i = 0;i <= coloum;i++) {
for (int j = 0;j <= coloum;j++) {
}
}
}
之后我们先让它们生成只有空格的7x7的正方形。为了方便识别,这里空格用"?"代替。
#include<stdio.h>
int main() {
int coloum = 7; // 定义菱形的宽(高)度
// i代表行, j代表列
for (int i = 0;i <= coloum;i++) {
printf("? ");
for (int j = 0;j <= coloum;j++) {
printf("? ");
}
printf("\n"); // 别忘了换行噢
}
}
生成的效果如下:
成功生成正方形之后,接下来思考如何在这个正方形之中来画出所需要的实心菱形。
再来观察一下应该得到的图形的样子:
想要在这个正方形内画出菱形,就要确认组成菱形的每一个星号所在的位置。
你会发现每一行至少都会有一个星号,而且最中间的列一定会有一个星号。 那么其余的星号也一定与这个中间列的星号有关。我们将这个中间列的星号定义为int mid = coloum/2+1,即中位数。
只要找到了星号位置的变化规律,代码的思路就出来了。
(注意:在C中对整数进行除运算时会向下取整。想要获得之中位数的话,需要在原来的值上+1)
从第4行看起,第1、第2、第3、第4行的星号数量都是呈金字塔状递增的;而第5、第6、第7行的星号数量呈金字塔状递减。那么说明在不同行之间,星号的变化规律并不完全一样。所以我们需要把行数分割为上半部分和下半部分,也就是说,我们要对i的取值分别讨论。
#include<stdio.h>
int main() {
int coloum = 7; // 定义菱形的宽(高)度
int mid = coloum / 2 + 1; // 获取菱形行(列)的中位数,也就是最中间行(或列)的位置
// i代表行, j代表列
for (int i = 0;i <= coloum;i++) {
if (i <= mid) { // 当行数处于上半区(包括最中间的行)时
for (int j = 0;j <= coloum;j++) {
printf("? ");
}
}else if (i > mid) { // 当行数处于下半区时
for (int j = 0;j <= coloum;j++) {
printf("? ");
}
}
printf("\n"); // 别忘了换行噢
}
}
我们将上半区单独分出来仔细观察:
在第一行中,星星所处的位置只有4。
在第二行中,星星所处的位置为3,4,5。即[3,5]这个区间。
在第三行中,星星所处的位置为2,3,4,5,6。即[2,6]这个区间。
在第四行中,星星所处的位置为1,2,3,4,5,6,7。即[1,7]这个区间。
不难发现,星星所处的位置为一个闭区间,分别为[4,4],[3,5],[2,6],[1,7]……即为4±0,4±1,4±2……
所以我们可以这样想:如果j(列数)处于这个区间内,那么输出“*”,否则输出空格。而且这些区间都是与菱形宽(高)度的中位数相关的。
即,星号的位置为一个区间。而对于上半区来说,这个区间为:[行的中位数(mid)-(当前行数-1),行的中位数(mid)+(当前行数-1)]。用代码实现便是:
for (int j = 1;j <= coloum;j++) {
if (j >= mid - (i - 1) && j <= mid + (i - 1)) {
printf("*");
}
else
printf(" ");
}
执行结果如下:
(因为还没处理下半部分代码,所以那里全是问号)
接下来我们来看下半区:
在第五行中,星号所处的位置为2,3,4,5,6,即[2,6]这个区间。
在第六行中,星号所处的位置为3,4,5,即[3,5]这个区间。
在第七行中,星号所处的位置只有4。
不难发现,星号所处的位置为一个闭区间,分别为[2,6],[3,5],[4,4]。即为4±2,4±1,4±0。星号的变化顺序刚好与前3行相反,为递减关系。
对于下半区来说,星号所处的区间规律为:[mid-(总行数-当前行数),mid+(总行数-当前行数)]。用代码实现就是:
for (int j = 1;j <= coloum;j++) {
if (j >= mid - (coloum - i) && j <= mid + (coloum - i)) {
printf("*");
}
else
printf(" ");
}
执行结果如下:
至此,代码已完成,可以实现题目要求的功能。可以自由改动column的值,生成任意大小的菱形。(注:coloum值必须为奇数,不然不能生成菱形)
打印实心菱形的完整源码如下:
#include<stdio.h>
int main() {
int column = 7; // 定义菱形的高度(宽度)
int mid = column / 2 + 1; // 获取菱形高(宽)的中位数
printf("%d\n", mid);
// i是行数 j是列数
for (int i = 1;i <= column;i++) { // 对行进行循环
if (i <= mid) { // 判断行数是在上方还是下方。这里是上方的情况(包括中间一行
for (int j = 1;j <= column;j++) { // 对列进行循环
// 判断单个列的情况
// 对上半区来说:
// 星星的位置为一个区间 即[行的中位数-(行数-1),行的中位数+(行数-1)]
if (j >= mid - (i - 1) && j <= mid + (i - 1)) {
printf("*"); // 输出星号
}
else
printf(" "); // 输出空格
}
}
else if (i > mid) { // 判断行数是否处于下方
for (int j = 1;j <= column;j++) {
// 判断单个列的情况
// 对下半区来说:
// 星星的位置为一个区间 即[行的中位数-(总行数-当前行数),行的中位数+(总行数-当前行数)]
if (j>=mid-(column-i) && j<=mid+(column-i)) {
printf("*"); // 输出星号
}
else
printf(" "); // 输出空格
}
}
printf("\n");
}
}
那么,通过上文的思路,打印空心菱形也不难。我们只需将j的判断语句改为等于最左边或最右边星号的位置即可。
打印空心菱形的完整源码如下:
#include<stdio.h>
int main() {
int coloum = 7; // 定义菱形的宽(高)度
int mid = coloum / 2 + 1; // 获取菱形行(列)的中位数,也就是最中间行(或列)的位置
// i代表行, j代表列
for (int i = 1;i <= coloum;i++) {
if (i <= mid) { // 当行数处于上半区(包括最中间的行)时
for (int j = 1;j <= coloum;j++) {
if (j == mid - (i - 1) || j == mid + (i - 1)) { // 将判断条件改为最左边的星号和最右边的星号即可
printf("*");
}
else
printf(" ");
}
}else if (i > mid) { // 当行数处于下半区时
for (int j = 1;j <= coloum;j++) {
if (j == mid - (coloum - i) || j == mid + (coloum - i)) { // 将判断条件改为最左边的星号和最右边的星号即可
printf("*");
}
else
printf(" ");
}
}
printf("\n"); // 别忘了换行噢
}
}
执行结果如下:
而如果需要打印实心心形的话,我们不妨借助上文的思路,通过上文的拆分来将心形拆分为上半部分和下半部分:
将图形往上移:
心形的结构比菱形稍微复杂一点,要多画几个心形来观察:
我们只需观察心形的上半部分(注:不包含最中间的那行)即可。来仔细观察上半部分:
仔细思考星号位置出现的规律,可以发现上半部分星号的位置属于这个区间:
[mid-当前行,当前行] ∪ [mid+mid-当前行,mid+当前行]
这个区间一样适用于没有任何星号的前几行。
那么,完整代码实现如下:
#include<stdio.h>
int main() {
int coloum = 11; // 定义心形的高度。必须为奇数
int mid = coloum / 2 +1; // 获取心形列数的中位数,也就是最中间列的位置。
// i代表行, j代表列
for (int i = 1;i <= coloum;i++) {
if (i < mid) { // 当行数处于上半区(不包括中间的行)时
for (int j = 1;j <= coloum;j++) {
if (( j>=mid-i && j<=i) || (j>=mid+mid-i && j<=mid+i )) { // 当星号处于 [mid-当前行,当前行] ∪ [mid+mid-当前行,mid+当前行] 区间时
printf("*");
}
else
printf(" ");
}
}else if (i >= mid) { // 当行数处于下半区(包括中间的行)时
for (int j = 1;j <= coloum;j++) {
if (j >= mid - (coloum - i) && j <= mid + (coloum - i)) { // 正常菱形下半区
printf("*");
}
else
printf(" ");
}
}
printf("\n"); // 别忘了换行噢
}
}
执行结果如下:
在网上搜了一下,没有具体的这种找规律然后用循环语句写出来的心形。心形上半区的规律真的很难找出来,我自认为我写得还是比较粗糙的。如果有大神有更好的思路的话,一定要不吝赐教!
总结:写代码一定要自己动手,多思考。动手和思考是最重要的,不要随随便便地就用百度找答案,多思考可以锻炼自己的思维,对日后的写代码都很有帮助。
2022/1/13:
今天看到有个视频有了更好的解法,不用分上下部分,也不用我这么复杂的判断。贴思路分享给大家:
这个图形在控制台输入时只由空格与星号组成,且是先打印空格,再打印星号。因此我们只要找出打印空格和星号的规律即可。
先初始化代码,写出大体的框架:
int column = 7; // 定义菱形的宽度
int mid = column / 2 + 1; // 定义中位数
int i, j; // i是行, j是列
for (i = 1;i <= column;i++) {
for (j = 1;j <= exep1;j++) { // exep1为空格列的判断语句
}
for (j = 1;j <= exep2;j++) { // exep2为星号列的判断语句
}
}
其中exep1、exep2分别为j处于空格列、星号列的判断语句。当语句满足对应的条件时,就输出空格或星号。因为菱形左边必定输出空格,剩余部分必定输入星号,因此需要两个j的for循环依次执行。
动手列个表,观察每一行空格、星号的个数规律:
发现空格的数量是|行数的中位数 - 行数|,星号的数量是(总行数-|中-行|*2。那么完整代码实现如下:
int column = 7; // 定义菱形的宽度
int mid = column / 2 +1; // 定义中位数
int i, j; // i是行, j是列
for (i = 1;i <= column;i++) {
for (j = 1;j <= abs(mid-i);j++) { // exep1为空格列的判断语句
printf(" ");
}
for (j = 1;j <= column-abs(mid-i)*2;j++) { // exep2为星号列的判断语句
printf("*");
}
printf("\n");
}
输出与上方菱形一致。
同理,想要用这个方法打印空心菱形的话,只需把星号列的判断条件改为“==”并加上j==1即可。
int column = 7; // 定义菱形的宽度
int mid = column / 2 +1; // 定义中位数
int i, j; // i是行, j是列
for (i = 1;i <= column;i++) {
for (j = 1;j <= abs(mid-i);j++) {
printf(" ");
}
for (j = 1;j <= column;j++) {
if (j == 1 || j == column - abs(mid - i) * 2) {
printf("*");
}
else {
printf(" ");
}
}
printf("\n");
}