【夯实基础的三道好题】

大家好呀,今天向大家分享3道比较有意思的题目,能够很好的帮助我们检验我们对一些知识的掌握程度,话不多说,直接上题。

 1 打印菱形

输入描述:

多组输入,一个整数(2~20)。

输出描述:

针对每行输入,输出用“*”组成的菱形,每个“*”后面有一个空格。

示例1

输入:

2

输出:

  * 
 * * 
* * * 
 * * 
  * 

示例2

输入:

3

输出:

   * 
  * * 
 * * * 
* * * * 
 * * * 
  * * 
   * 

复制

示例3

输入:

4
输出:
    * 
   * * 
  * * * 
 * * * * 
* * * * * 
 * * * * 
  * * * 
   * * 
    * 

这道题主要考察我们对循环的掌握情况。我们可以这样考虑:将菱形分为上半部分和下半部分,例如我们输入2,就将前两行分为上半部分,后三行分为下半部分。而上半部分我们又可以这样来考虑:每一行我们要打印的内容分成两个部分,一个为空格,另一个为*。有了这个思路我们就可以直接动手写代码了。

int main()
{
	int x = 0;
	while (~scanf("%d", &x))
	{
		int i = 0;
		int j = 0;
		//上半部分的打印
		for (i = 0; i < x; i++)
		{
			//打印空格
			for (j = 0; j < x - i; j++)
			{
				printf(" ");
			}
			//打印*
			for (j = 0; j <= i; j++)
			{
				printf("* ");
			}
			//换行
			printf("\n");
		}
		//下半部分的打印
		for (i = 0; i < x + 1; i++)
		{
			//打印空格
			for (j = 0; j < i; j++)
			{
				printf(" ");
			}
			//打印*
			for (j = 0; j < x + 1 - i; j++)
			{
				printf("* ");
			}
			//换行
			printf("\n");
		}
	}
	return 0;
}

结果展示:

417f008f568d4dcd8664891718798333.png

 2 旋转字符串(左旋)

给定一个字符串arr,假设里面存放ABCDE,输入一个数x(小于字符串的长度),例如1,arr里面就变为BCDEA,输入2,arr里面就变为CDEAB。

方法一:暴力求解法

//method 1(暴力求解法)
void left_move(char* arr, int x)
{
	int len=strlen(arr);
	assert(x <= len);
	assert(arr);
	for (int i = 0; i < x; i++)
	{
		char tmp = *arr;
		for (int j = 0; j < len-1; j++)
		{
			arr[j] = arr[j + 1];
		}
		arr[len - 1] = tmp;
	}
}
int main()
{
	char arr[20] = "abcdef";
	int x = 0;//x是要左旋几个数
	scanf("%d", &x);
	left_move(arr, x);
	printf("%s", arr);
	return 0;
}

暴力求解法中求解思路是一个一个的移动,但是时间复杂度较大,我们一般不采取这种方法。

方法二:三步反转法

在ABCDEF中,假设我们要左旋两个数,不妨将ABCDEF分成两个部分:AB   和   CDEF,先将AB逆序,得到BA,再将CDEF逆序,得到FEDC,再将BAFEDC合并,然后再逆序,得到CDEFAB.这个就是我们要的最终结果。其实先逆序两个部分再逆序整体与先逆序两个部分效果是一样的。

有了思路后就可以写代码了:

//method 2(三步翻转法)
void reverse(char* left, char* right)
{
	assert(left!=NULL);
	assert(right!=NULL);
	while (left < right)
	{
		char tmp = *left;
		*left = *right;
		*right = tmp;
		left ++ ;
		right--;
	}
}
void left_move(char* arr, int x)
{
	assert(arr);
	int len = strlen(arr);
	reverse(arr, arr + x - 1);
	reverse(arr + x, arr + len - 1);
	reverse(arr, arr + len -1);
}
int main()
{
	char arr[20] = "ABCDEF";
	int x = 0;
	scanf("%d", &x);
	left_move(arr, x);
	printf("%s", arr);
	return 0;
}

其中reverse是一个逆序函数。

3 查找杨氏矩阵中元素

什么是杨氏矩阵?简单的来说,就是每一行从左到右的元素依次增大,每一列从上到下的元素依次增大。题目要求时间复杂度小于o(n),那么一个一个查找肯定是行不通的,那么我们应该怎样来查找呢?

我们首先得给定一个二维数组arr[3][3]={{1,2,3},{4,5,6},{7,8,9}}

为了方便理解我们就将其展示成如下模样:

       1  2  3

       4  5  6

       7  8  9

我们会发现右上角这个元素有些特殊(左下角也行),假设我们要查找的这个元素是7,我们那这个数与3比较,大于3,那么3这个元素所再的那一行就可以pass。向下移动一个元素,找到6,6也小于7,6所在的那一行也pass.再向下移动一个元素,找到9,9大于7,9所在的那一列pass,然后向左移动一个元素,找到8,8大于7,8所在的那一列pass.最后向左移动一个元素,就找到了7.总共来看我们一共找了5次,满足题意。

有了这个理解,我们就可以写代码了:

//查找杨氏矩阵中的元素,要求时间复杂度小于O(N)
int Find_Num(int (*p)[3], int row, int col, int k)
{
	int x = 0;
	int y = col - 1;
	while (x <= row - 1 && y >= 0)
	{
		if (k > p[x][y])
			x++;
		else if (k < p[x][y])
			y--;
		else
			return 1;
	}
	return 0;
}
int main()
{
	int arr[3][3] = { {1,2,3},{4,5,6},{7,8,9}};
	int k = 5;
	//scanf("%d", &k);
	int ret = Find_Num(arr, 3, 3,k);
	if (1 == ret)
		printf("找到了\n");
	else
		printf("找不到\n");
	return 0;
}

有人可能会问:我们想知道被查找元素的下标咋办?向Find_Num中return 1上一行加上printf输出x和y就好了,这样做没有问题,但是在Find_Num函数中加一个printf会使该函数有点挫,那么有没有更好的办法呢?

答案是有的,我们可以用返回性参数来处理这个问题:

//升级版
int Find_Num(int(*p)[3], int *px, int *py, int k)
{
	int x = 0;
	int y = *py - 1;
	while (x <= *px - 1 && y >= 0)
	{
		if (k > p[x][y])
			x++;
		else if (k < p[x][y])
			y--;
		else
		{
			*px=x;
			*py=y;
			return 1;
		}
	}
	return 0;
}
int main()
{
	int arr[3][3] = { {1,2,3},{4,5,6},{7,8,9} };
	int k = 0;
	scanf("%d", &k);
	int x = 3; //返回型参数
	int y = 3;
	int ret = Find_Num(arr, &x, &y, k);
	if (1 == ret)
	{
		printf("找到了\n");
		printf("下标是:%d %d", x, y);
	}
	else
		printf("找不到\n");
	return 0;
}

代码中可以看到:将x和y的地址作为参数传给了函数Find_Num,这样有什么好处呢?当我们在Find_Num函数中通过对x和y的地址解引用并且改变解引用后的值时,main函数中,x与y的值也会随之改变,我们将x,y的值打印出来就是想要的坐标。

结果展示:

c594d040c46846c19c3b31c05d42d92f.png

如果上面这3道题对你有所帮助的话,能不能3连支持一下博主,谢谢啦~

d3a419d02fe84fb39ac934bf7a04766c.png

 

 

评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值