每日一题|算法|三种蛇形排序|什么才是真正的简洁高效啊(战术后仰)

        最近正在看紫书,经典不愧为经典,这本书中简洁、高效、一针见血而又不失可读性的代码,让笔者看得大呼过瘾,这才是真正的编程思维,之前写的那坨简直就是屎山。正好看到了蛇形排序这一题目,看到网上的三种排序,笔者也想动动手,尝试使用紫书中的编程风格,将代码敲出来。

        此文章图片部分引用自三种蛇形填数-CSDN博客,在此基础上大幅优化代码。

        

第一种蛇形排序:s形

在这里插入图片描述

三种排序中最简单的一种。

#include "stdio.h"
#include "string.h"
#define max 20
int a[max][max];
int main(void)
{
    int x,y,n,tot = 0;
    memset(a,0,sizeof(a));
    tot = a[x = 0][y = 0] = 1;
    scanf_s("%d",&n);
//排序
    while (tot < n * n)
    {
        while (x < n - 1) a[ ++x ][y] = ++ tot;
        a[x][ ++y ] = ++ tot;
        while (x > 0) a[ --x ][y] = ++ tot;
        a[x][ ++y ] = ++ tot;
    }
//输出
    for (x = 0;x < n;x ++)
    {
        for (y = 0;y < n;y ++) printf("%3d",a[x][y]);
        printf("\n");
    }
    return 0;
}

        兄弟们,简不简洁?除却必要的输入和输出,核心的排序部分仅仅需要7行代码,如果将while循环的括号忽略不计,甚至可以压缩到5行。接下来开始逐一讲解。

1.memset函数?

        一种快速填充数组的代码,需要使用头文件string.h(不要问我为什么要用string头文件,可能是老丹在写字符数组的时候做了这个函数吧)。这里我们使用它对数组进行初始化0。虽然使用for循环也能实现相同的效果,但memset又方便又快捷。

2.准备工作?

tot = a[x = 0][y = 0] = 1;

        啧啧啧,看这小代码,多简洁,多高效。同时完成了四件事:为x赋值、为y赋值、为数组的第一格填充数字、为tot赋值。一个字,牛。 

3.核心排序?

while (tot < n * n)
    {
        while (x < n - 1) a[ ++x ][y] = ++ tot;
        a[x][ ++y ] = ++ tot;
        while (x > 0) a[ --x ][y] = ++ tot;
        a[x][ ++y ] = ++ tot;
    }

        大循环的条件为什么是tot < n*n?因为在这里,tot既充当填充数组的角色,又对数组的排序进行计数,一量两用。当tot = n*n时,数组的填充正好完毕,也就退出循环。

        第一行代码的作用:向下填充,并且 ++x的作用是最后一格不会填充,但x正好到了最后一格(不懂的自己去翻课本),留给下一行的向右填充的代码使用。而++tot同理,先加再填,与初始化为1的前期工作完美对应。

while (x < n - 1) a[ ++x ][y] = ++ tot;

        第二行代码:向右填充。由于x刚好在最后一格,我们只需要 ++y即可,该代码完成后,我们不仅完成了对最后一格代码的填充,而且向右移了一格,为接下来的向上填充做准备。

a[x][ ++y ] = ++ tot;

        之后的两行代码异曲同工,就不过多赘述。

第二种蛇形排序:螺旋形

 

也是紫书上的题目。笔者放上紫书的代码,大家感受一下初见此代码的震撼。

#include<stdio.h>
 
#include<string.h>
 
#define maxn 20
 
int a[maxn][maxn];
 
int main()
 
{
 
    int n, x, y, tot = 0;
 
    scanf("%d", &n);
 
    memset(a, 0, sizeof(a));
 
    tot = a[x=0][y=n-1] = 1;    //赋值完成后立即作为下标使用
 
    while(tot < n*n)
 
    {
 
        while(x+1<n && !a[x+1][y]) a[++x][y] = ++tot;  //先判断再移动,下一个格子是x+1,所以对x+1进行判断;即对a[x+1][y]这个格子进行判断
 
        while(y-1>=0 && !a[x][y-1]) a[x][--y] = ++tot;
 
        while(x-1>=0 && !a[x-1][y]) a[--x][y] = ++tot;
 
        while(y+1<n && !a[x][y+1]) a[x][++y] = ++tot;
 
    }
 
    for(x = 0; x < n; x++)
 
    {
 
        for(y = 0; y < n; y++) printf("%3d", a[x][y]);
 
        printf("\n");
 
    }
 
    return 0;
 
}

其中的四行核心思路,与前一题相同,但是工整了一些。笔者有些小小的建议:将这四行代码改动为

        while (x < n - 1 && a[x + 1][y] == 0) a[ ++x ][y] = ++ tot;
        while (y > 0 && a[x][y - 1] == 0) a[x][ --y ] = ++ tot;
        while (x > 0 && a[x - 1][y] == 0) a[ --x ][y] = ++ tot;
        while (y < n - 1 && a[x][y + 1] == 0) a[x][ ++y ] = ++ tot;

我们不仅需要简洁,也需要可读性。!a[x + 1][y]虽然更简单,但是其中的逻辑难懂,我们应该做到简洁和可读性的兼顾,判断是否为0显然易懂许多。

第三种蛇形排序:三角形

这道题曾在蓝桥杯上出现过,乍一看很吓人,实际和前两道没什么本质的区别。仍需记住核心的观念:简洁,充分利用c语言代码的特性。

#include "stdio.h"
#include "string.h"
#define max 20
int array[max][max];
int main()
{
    int tot,x,y,i = 0,n;
    memset(array,0,sizeof(array));
    scanf_s("%d",&n);
    int ni = n;
    tot = array[x = 0][y = 0] = 1;
    while(tot < (n*n - n)/2 + n)
    {
        array[x][++y] = ++tot;
        while(y > 0) array[++x][--y] = ++tot;
        array[++x][y] = ++tot;
        while(x > 0) array[--x][++y] = ++tot;
    }
    for (x = 0;x < n;x ++)
    {
        for (y = 0;y < ni;y++) printf("%3d",array[x][y]);
        printf("\n");
        ni--;
    }
    return 0;
}

唯一要说的可能就是while的循环判定,tot这里不是简单的n*n,我们可以看到它其实只占了半数。当然,一旦理解了代码,我们可以将其改为n*n,这样三角形填数就填满了整个屏幕;还可以在输出的时候选择半数输出和全输出,还是那句话,真正理解了代码后,我们就有了多样化的选择。

推荐

最后再提一嘴,这本紫书看得太爽了,笔者强烈推荐这本书。笔者的IDE是Clion,算是代码联想和报warning比较完备的一款IDE,当敲出来如此简洁的代码,右上角显示出一个小小的“√”(0 warning 0 error)时,才意识到我们平常在刷题时,通过题目不是我们的唯一目的。如何提升我们的思维,充分运用各个语言不同的优势(比如c语言代码的简洁性),简洁高效地敲出代码,才是我们的最终目标。

over~

  • 41
    点赞
  • 34
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值