【C语言刷题】可能难住过你的三道有趣打印题 - 回形矩阵、蛇形矩阵、Z字形变换

前言

这三道题目均来自牛客网力扣

本文用图解的方式,以最简单的思路带你手把手解决这三个问题,但更重要的是要学会举一反三。

当然,三个题的解法肯定都不唯一,我这里给出来的解法可能并不是最优,只是以相对简单清晰的思路去分析问题,所以没有考虑复杂度的问题。


回型矩阵

题目描述

在这里插入图片描述

问题分析

先看一张图:
在这里插入图片描述
对于 n 为奇数和偶数,两个矩阵还不一样。
先看两种矩阵的共同点:

由内而外,先填外圈后填内圈,所以把填一圈抽象成一个大循环,每一个大循环又包含了四个小循环,每个小循环分别填一条边。
所以只需写出每个循环即可解决问题。

两种矩阵还有不同点:

对于 n 为偶数的情况,走完整数次大循环即可填满整个矩阵。
而对 n 为奇数的情况,走完整数次大循环之后会空下中间一个位置。
这个问题可以通过动态边界来解决:
定义两个边界 minmax ,没走完一次大循环让 min++max–
如果 n 为偶数,经过整数次大循环之后 min > max ,
如果 n 为奇数,经过整数次大循环之后 min = max
所以可以利用这一点加个判断条件填上中间那个元素。

代码实现

int main()
{
    int n = 0;
    scanf("%d", &n);   
    int i, j;
    
    //根据输入值n分配一块空间给二维数组
    int** arr = malloc(sizeof(int*) * n);
    for (i = 0; i < n; i++)
        arr[i] = (int*)malloc(sizeof(int) * n);
    
    //计数器和边界的定义与初始化
    int count = 1; 
    int min = 0, max = n - 1;

	//大循环,跳出条件为 count < n * n
    while (count < n * n)
    {
    	//第一次小循环 - 填充上边
    	//每次填充完行号不变,列号+1,直至列号碰到边界max
        for (i = min; i < max; i++)
            arr[min][i] = count++;
            
        //第二次小循环 - 填充右边
        //每次填充完列号不变,行号+1,直至行号碰到边界max
        for (i = min; i < max; i++)
            arr[i][max] = count++;
            
        //第三次小循环 - 填充下边
        //每次填充完行号不变,列数-1,直至列号碰到边界min
        for (i = max; i > min; i--)
            arr[max][i] = count++;
            
        //第四次小循环 - 填充左边
        //每次填充完列号不变,行号-1, 直至行号碰到边界min
        for (i = max; i > min; i--)
            arr[i][min] = count++;
        
        //每次大循环结束边界缩减
        min++;
        max--;
    }

    //填补 n 为奇数时的中间元素
    if (min == max)
        arr[min][max] = count;
        
    for (i = 0; i < n; i++)
    {
        for (j = 0; j < n; j++)
            printf("%d ", arr[i][j]);
        printf("\n");
    }
    free(arr);
    return 0;
}

蛇形矩阵

题目描述

在这里插入图片描述

问题分析

相较上一个问题,这个就比较单纯了,没涉及到奇偶数。

还是先看图:
(图片与牛客网上的一篇讨论极为相像,因为那篇讨论是我写的)
在这里插入图片描述
每个折线形状都相同,都有三条路径,那么只需实现三条路径上的打印,然后放在 while 大循环内即可。

但是看图可以发现,循环难免会出现越界的情况,那么只需在赋值前加个判断条件即可,即不越界才能赋值。

代码实现

int main()
{
    int n = 0;
    scanf("%d", &n);
    int i, j;

    //老样子,分配一块空间给二维数组
    int** arr = malloc(sizeof(int*) * n);
    for (i = 0; i < n; i++)
        arr[i] = (int*)malloc(sizeof(int) * n);

    //计数器和行号列号
    int count = 1;
    int row = 0;
    int col = 0;

    //一个大循环,内部实现三条路径上的打印
    while (count <= n * n)
    {
    
        //第一个路径,只包含两个元素,任何时候都是
        //每次执行完行号不变,其实都是0,列号+1
        for (int i = 0; i < 2; i++)
        {
            if (row < n && col < n)
                arr[row][col] = count++;
            col++;
        }

		//第一个路径打印完列号会超出一个,给它抓回来
        col--;

		//第二个路径
		//每次执行完行号+1,列号-1
        while (col)
        {
            row++;
            col--;
            if (row < n && col < n)
                arr[row][col] = count++;
        }

		//第二个路径打印完行号会超,给它拽回来
        row++;

		//第三个路径
		//每次执行完行号-1,列号+1
        while (row)
        {
            if (row < n && col < n)
                arr[row][col] = count++;
            row--;
            col++;
        }
        
    }
    
    for (i = 0; i < n; i++)
    {
        for (j = 0; j < n; j++)
            printf("%d ", arr[i][j]);
        printf("\n");
    }
    free(arr);
    return 0;
}

Z字形转换

题目描述

在这里插入图片描述
在这里插入图片描述

问题分析

这个题有个比较坑的地方就是 示例3 给的那种情况,numRows = 1,此时字符串实际上没变,直接返回即可,所以把它列为一种特殊情况处理。

一般情况还是看图:
在这里插入图片描述
通过图可以发现这何尝不是一种循环呢,而且每次循环的路径还都一样。

只需要实现两个路径,把它放入 while 循环,当完全遍历字符串内容之后跳出循环即可完成。

设置一个二维数组存放字符,遍历字符串之后,再遍历一遍二维数组重新对 s 进行赋值并将其返回。

代码实现

char * convert(char * s, int numRows)
{
	//处理特殊情况
    if(numRows == 1)
        return s;
        
    int len = strlen(s); //统计字符串长度
    int row = 0; //行号
    int col = 0; //列号
    int pos = 0; //指向字符串 s 的第 pos 个元素,每次
                 //执行后 pos++,当 pos = len 时跳出循环。
    //定义一个二维数组接收字符,由于不知道具体大小,就不动态内存分配了。
    char ch[1000][1000] = {0};
    
    while(pos < len)
    {
    
    	//第一条路径 - 竖着下来
    	//每次赋值完行号+1,列号不变
        while(row < numRows)
        {
            if(pos == len)
                break;
            ch[row++][col] = s[pos++];
        }

		//走完第一条路径后行号会超,给它拽回来
        row--;

		//第二条路径 - 斜着上去
		//每次行号-1,列号+1
        while(row > 1)
        {
            if(pos == len)
                break;
            ch[--row][++col] = s[pos++];
        }

		//将行号列号指向下一次循环开始的位置
        row--;
        col++;
        
    }

	//重新对字符串 s 进行排列
    pos = 0;
    for(int i=0; i<numRows; i++)
        for(int j=0; j<col; j++)
            if(ch[i][j] != 0)
                s[pos++] = ch[i][j];
    return s;
}

小尾巴

最后,还是那句话,上面的解法不一定最优,但一定最好理解。
所以如果有更好的想法可以留言噢~

封面图送上:
在这里插入图片描述

  • 13
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

LeePlace

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

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

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

打赏作者

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

抵扣说明:

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

余额充值