zoukankan      html  css  js  c++  java
  • 趣味编程螺旋打印

    描述

    给定一个自然数n,打印1-n之间所有的数,要求:按螺旋形状顺时针打印。以前看到过这道题,说的是从外向内螺旋打印,而前几天又看到一个变种,由内向外打印。比之前的稍微难一点,趁周末闲着没事,总结一下。先上两幅图,大家看一下效果。

    由外向内打印

    由内向外打印

    这两种输出方法,其实大同小异,道理都差不多,会了一种,则另一种不难,先看从外向内打印的

    分析

    最简单且直观的方法就是k * k的二维数组存储数字,先将数字按照要求填入数组,然后输出整个数组即可

    分配数组

    对于n个数而言,令k = Ceil(sqrt(n)), 则分配k*k的二维数组即可。比如n = 5时,分配3*3的数组即可。

    如何填数

    起始点

    以左上角为起始点

    方向

    用一个整数flag来标识方向

    flag = 1 - 向右

    flag = 2 - 向下

    flag = 3 - 向左

    flag = 4 - 向上

    边界判断

    当到达边界的时候应该变换方向,即向右转,如何判断边界?我的方法是,将所有数组元素初始化为-1,则有如下两种情况

    1.下标超出二维数组边界,则需转向(注意,转向之前下标需要退回一格)

    2.下标未越界,但下一个位置的值不是-1,那么说明它被填充过,也需转向。

    填充

    重复以下过程直到所有数据填充完毕。

    1. 从左至右填充数组,如遇右边界或者下一位置已经被填充过,则改变方向,转入步骤2

    2. 从上至下填充数组,如遇下边界或者下一位置已经被填充过,则改变方向,转入步骤3

    3. 从右至左填充数组,如遇左边界或者下一位置已经被填充过,则改变方向,转入步骤4

    4. 从下至上填充数组,如遇上边界或者下一位置已经被填充过,则改变方向,转入步骤1

    整个填充过程如下图

     

    代码

    有了以上思路,则写代码不是难事

    代码
    1 // 从外向内螺旋打印1-n
    2  void SpiralPrint1(int n)
    3 {
    4 // 计算二维数组的维数k
    5   int k = ceil(sqrt((float)n)) ;
    6
    7 // 动态分配二维数组
    8   int **a = new int*[k] ;
    9 for(int m = 0; m < k; m++)
    10 {
    11 a[m] = new int[k] ;
    12 }
    13
    14 // 初始化数组元素值为-1
    15   for(int x = 0; x < k; ++x)
    16 {
    17 for(int y = 0; y < k; ++y)
    18 {
    19 a[x][y] = -1 ;
    20 }
    21 }
    22
    23 // 起始数字
    24   int num = 1 ;
    25
    26 // 方向标志,起初向右
    27   int flag = 1 ;
    28
    29 // 起始位置
    30 int i = 0;
    31 int j = 0 ;
    32
    33 // 填数
    34 while(true)
    35 {
    36 // 所有数都填完了么?
    37 if(num > n)
    38 break ;
    39
    40 // 如果当前位置尚未填充,则填充
    41 if(a[i][j] == -1)
    42 a[i][j] = num++ ;
    43
    44 // 寻找下一个可填充位置
    45
    46 // 从左向右
    47 if(flag == 1)
    48 {
    49 j++ ;
    50 if(j == k || a[i][j] != -1) // 到达右边界或已经填充过
    51 {
    52 j-- ;// 列下标会退
    53 flag = 2 ; // 到达边界则向下
    54 }
    55 }
    56
    57 // 从上到下
    58 else if(flag == 2)
    59 {
    60 i++ ;
    61 if(i == k || a[i][j] != -1)// 到达下边界或已经填充过
    62 {
    63 i-- ; // 行下标回退
    64 flag = 3 ; //转向左
    65 }
    66 }
    67
    68 // 从右向左
    69 else if(flag == 3)
    70 {
    71 j-- ;
    72 if(j == -1 || a[i][j] != -1)// 到达左边界或已经填充过
    73 {
    74 j++ ; // 列下标前进
    75 flag = 4 ; // 到达边界则向上
    76 }
    77 }
    78
    79 // 从下向上
    80 else // flag == 4
    81 {
    82 i-- ;
    83 if(i == -1 || a[i][j] != -1)// 到达上边界或已经填充过
    84 {
    85 i++ ; // 行下标前进
    86 flag = 1 ; // 到达边界则向右
    87 }
    88 }
    89 }
    90
    91 // 输出
    92 for(int i = 0; i < k; i++)
    93 {
    94 for(int j = 0; j < k; j++)
    95 {
    96 cout << right << setw(3) ;
    97
    98 if(a[i][j] != -1)
    99 cout << a[i][j] ;
    100 else
    101 cout << " " ;
    102 }
    103 cout << endl ;
    104 }
    105 }

    由内向外填充

    如果上面的搞懂了,则这个也就不难了,不过有一些细节上的东西还需修改一下

    起始位置

    由于是从内向外填充,所以起始位置不再是(0, 0),需要重新计算,而且还与二维数组的阶数k的奇偶性相关

    1 当k为奇数时,起始位置为(k / 2, k / 2),如下:

    k = 3,起始位置为(1, 1)
    7  8  9
    6  1  2
    5  4  3

    2 当k为偶数时,起始位置为(k / 2 - 1, k / 2 - 1),如下:

    k = 4,起始位置为(1, 1)
    7  8  9 10
    6  1  2 11
    5  4  3 12
    16 15 14 13

    边界判断

    与从外向内填充不同的是,边界判断中不会再出现下标越界的情况,只需判断当前位置是否填充过即可

    方向转换

    从内向外填充时,能转向时优先转向,不能转向时才继续向前填充,而从外向内填充则恰恰相反,无法继续向前填充时才转向。

    代码

    代码
    1 // 从内向外螺旋打印1-n
    2 void SpiralPrint(int n)
    3 {
    4 // 计算二维数组的维数k
    5 int k = ceil(sqrt((float)n)) ;
    6
    7 // 动态分配二维数组
    8 int **a = new int*[k] ;
    9 for(int m = 0; m < k; m++)
    10 {
    11 a[m] = new int[k] ;
    12 }
    13
    14 // 初始化数组元素值为-1
    15 for(int x = 0; x < k; ++x)
    16 {
    17 for(int y = 0; y < k; ++y)
    18 {
    19 a[x][y] = -1 ;
    20 }
    21 }
    22
    23 // 起始数字
    24 int num = 1 ;
    25
    26 // 方向标志,起初向上
    27 int flag = 4 ;
    28
    29 // 起始位置,视k的奇偶性而定
    30 int i ;
    31 int j ;
    32
    33 if(k & 1)
    34 {
    35 i = k / 2 ;
    36 j = k / 2 ;
    37 }
    38 else
    39 {
    40 i = k / 2 - 1 ;
    41 j = k / 2 - 1 ;
    42 }
    43
    44 // fill in the array
    45 while(true)
    46 {
    47 // 所有数都填完了么?
    48 if(num > n)
    49 break ;
    50
    51 // 如果当前位置尚未填充,则填充
    52 if(a[i][j] == -1)
    53 a[i][j] = num++ ;
    54
    55 // 寻找下一个可填充位置
    56
    57 // 从左向右
    58 if(flag == 1)
    59 {
    60 if (a[i + 1][j] == -1) //下边可填充则转向下
    61 {
    62 i++ ;
    63 flag = 2 ;
    64 }
    65 else
    66 j++ ; //否则继续向右
    67 }
    68
    69 // 从上到下
    70 else if(flag == 2)
    71 {
    72 if(a[i][j - 1] == -1) //左边可填充则转向左
    73 {
    74 j-- ;
    75 flag = 3 ;
    76 }
    77 else
    78 i++ ; //否则继续向下
    79 }
    80
    81 // 从右向左
    82 else if(flag == 3)
    83 {
    84 if(a[i - 1][j] == -1) //上边可填充则转向上
    85 {
    86 i-- ;
    87 flag = 4 ;
    88 }
    89 else
    90 j-- ;
    91 }
    92
    93 // 从下向上
    94 else // flag == 4
    95 {
    96 if (a[i][j + 1] == -1) //右边可填充则转向右
    97 {
    98 j++ ;
    99 flag = 1 ;
    100 }
    101 else
    102 i-- ; //否则继续向上
    103 }
    104 }
    105
    106 // 输出
    107 for(int i = 0; i < k; i++)
    108 {
    109 for(int j = 0; j < k; j++)
    110 {
    111 cout << right << setw(3) ;
    112
    113 if(a[i][j] != -1)
    114 cout << a[i][j] ;
    115 else
    116 cout << " " ;
    117 }
    118 cout << endl ;
    119 }
    120 }
  • 相关阅读:
    曾经写的一些文章,与技术无关,整理出来怀旧,:)
    在VS.NET2003中使用XHTML的插件HTML TIDY 及 MindManger
    把机器退出了域,造成无法启动 MSSQLSERVER ,晕
    hello php!
    今天又看了一下存储过程
    [转]PAGEII携手极速网爱情电影经典对白
    关键词:2005年,世乒赛.上海,乒乓的胜地
    一本SharePoint方面的书,Special Edition Using Microsoft® SharePoint Portal Server
    keo计划
    关于xp_cmdshell 。。注意安全!
  • 原文地址:https://www.cnblogs.com/graphics/p/1739658.html
Copyright © 2011-2022 走看看