常规方法和曼哈顿距离法打印菱形

目录

问题描述

常规方法分析

打印上半部分的代码

对打印下半部分的分析

打印下半部分的代码

曼哈顿距离法


问题描述

给定行数,输出菱形。例如,输入13时,输出以下菱形。

常规方法分析

对于打印菱形,我们可以把菱形一分为二找规律,也就是说,分别对上半部分和下半部分分析,找到它们各自的规律。上半部分相对来说比较简单,规律比较明显,我们首先来分析上半部分,找到其规律。

我们只需关注左边的空格数,右边的空格不用考虑。

设行号为 i ,上半部分行数为line_up, 则星号*的数量 = 2 * i + 1,空格数 = line_up - 1 - i

由此,我们得出常规打印菱形上半部分的方法。为了便于理解,下面将分别给出打印上半部分和打印下半部分的代码以及完整代码。

打印上半部分的代码

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

int main()
{
	int line_all = 0;//总行数
	int line_up = 0;//上半部分行数
	int line_down = 0;//下半部分的行数
	printf("请输入行数(奇数):");
	scanf("%d", &line_all);

	line_up = line_all / 2 + 1;
	line_down = line_all - line_up;

	//打印上半部分

	int i = 0;//i表示第几行
	for (i = 0; i < line_up; i++)
	{
		//先打印空格
		int j = 0;
		for (j = 0; j < line_up - 1 - i; j++)
		{
			printf(" ");
		}

		//打印*
		for (j = 0; j < 2 * i + 1; j++)
		{
			printf("*");
		}
		printf("\n");
	}

	return 0;
}
对打印下半部分的分析

 

通过观察行号和空格这两列, 我们发现:空格数始终比行号多1,所以得出规律:

空格数  = 行号 + 1 

接下来我们分析如何打印星号。

以红色中线为界,分为左右两半,设下半部分的行数为line_down,行号依然为i。

7 = (4 - 1 - 0)* 2 + 1

4 - 1 - 0即line_down - 1 - i, 式子最后+1,1是指红色分割线上的星号

5 = (4 - 1 -1)* 2 + 1

4 - 1 - 1为 line_down - 1 - i, i 此时为1

……

综上,其规律为:星号数 = (line_down - 1)* 2 + 1

打印下半部分的代码
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

int main()
{
	int line_all = 0;//总行数
	int line_up = 0;//上半部分行数
	int line_down = 0;//下半部分的行数
	printf("请输入行数(奇数):");
	scanf("%d", &line_all);

	line_up = line_all / 2 + 1;
	line_down = line_all - line_up;

	//打印下半部分
	int i = 0;//第几行
	for (i = 0; i < line_down; i++)
	{
		//打印空格
		int j = 0;
		for (j = 0; j < i + 1; j++)
		{
			printf(" ");
		}

		//打印星号
		for (j = 0; j < (line_down - 1 - i) * 2 + 1; j++)
		{
			printf("*");
		}

		printf("\n");
	}


	return 0;
}

常规方法完整代码:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>

void Print_up(int line_up);//声明打印上半部分的函数
void Print_down(int line_down);//声明打印下半部分的函数

int main()
{
	int line_all = 0;//总行数
	int line_up = 0;//上半部分行数
	int line_down = 0;//下半部分行数

	printf("请输入总行数(奇数)");
	scanf("%d", &line_all);

	line_up = line_all / 2 + 1;
	line_down = line_all - line_up;

	Print_up(line_up);//打印上半部分
	Print_down(line_down);//打印下半部分

	return 0;
}

void Print_up(int line_up)
{
	int i = 0;
	for (i = 0; i < line_up; i++)
	{
		//打印空格
		int j = 0;
		for (j = 0; j < line_up - 1 - i; j++)
		{
			printf(" ");
		}
		//打印星号
		for (j = 0; j < 2 * i + 1; j++)
		{
			printf("*");
		}

		printf("\n");
	}
}

void Print_down(int line_down)
{
	int i = 0;//第几行
	for (i = 0; i < line_down; i++)
	{
		//打印空格
		int j = 0;
		for (j = 0; j < i + 1; j++)
		{
			printf(" ");
		}

		//打印星号
		for (j = 0; j < (line_down - 1 - i) * 2 + 1; j++)
		{
			printf("*");
		}

		printf("\n");
	}
}


曼哈顿距离法

首先来认识一下什么是曼哈顿距离。

图中红线代表曼哈顿距离,绿色代表欧氏距离,也就是直线距离,而蓝色和黄色代表等价的曼哈顿距离。曼哈顿距离——两点在南北方向上的距离加上在东西方向上的距离,即d(i,j)=|xi-xj|+|yi-yj|。

在平面上,坐标(x1,y1)的 i 点与坐标(x2,y2)的 j 点的曼哈顿距离为:

d(i,j)=|x1-x2|+|y1-y2|.

也就是两点之间的曼哈顿距离等于两点横坐标差的绝对值加上纵坐标差的绝对值

在打印菱形前我们得确定一个中心点,然后分别算出每一点到中心点的曼哈顿距离,即可找出规律。

下面以打印一个五行的菱形为例。

中心点的横坐标用center_x表示,纵坐标用center_y表示,总行数用 n 表示,本例 n = 5.

center_x = n / 2;

center_y = n / 2;

所以本例中,中心点坐标为(2,2)。下面依次计算各点到中心点的曼哈顿距离。

通过观察,我们可以得出几点规律。

> 要打印的菱形有几行,我们就可以创建以菱形总行数为边长的正方形来容纳,即创建二维数组。

> 如果该点到中心点的曼哈顿距离小于等于2,即小于等于2/n时,打印星号,否则,打印空格。

综上,可以得出代码实现的思路。

首先,我们得创建一个二维数组用来存放菱形,其次遍历数组中的每一个元素,把每一个元素的坐标与中心点坐标的曼哈顿距离求出。最后,把曼哈顿距离小于等于n/2的元素置为星号,其余的置为空格。

曼哈顿距离法代码

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <math.h>

int main()
{
	int n = 0;//表示总行数
	printf("请输入总行数(奇数):");
	scanf("%d", &n);

	int center_x = n / 2;//中心点的横坐标
	int center_y = n / 2;//中心点的纵坐标
	int i = 0;//行号
	int j = 0;//列号

	//此处可以理解为创建了二维数组,你可能会有疑问:为什么不这样写 arr[n][n]?请看下文。
	for (i = 0; i < n; i++)
	{
		for (j = 0; j < n; j++)
		{
			if ((fabs(i - (double)center_y) + fabs(j - (double)center_x)) <= (n / 2))
				printf("*");
			else
				printf(" ");
		}
		printf("\n");
	}
	return 0;
}

//因为fabs的参数类型为double,所以进行了强制类型转换,以免出现警告
//之所以不直接int arr[n][n]创建二维数组,是因为这是一个变长数组,长度只有
// 在程序运行时才能确定,
//但是VS目前并不支持变长数组,所以选择了上面的方式

比较难理解的可能就是所谓二维数组的创建,之所以提到二维数组,一方面是因为遍历的方式与二维数组的遍历方式很相似,其次是因为方便理解坐标之间的关系,便于理解并计算曼哈顿距离。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值