黑马程序员技术交流社区
标题: 挑战题异常求解答 [打印本页]
作者: 戴振良 时间: 2013-5-2 20:59
标题: 挑战题异常求解答
本帖最后由 戴振良 于 2013-5-5 17:10 编辑
很惭愧呀,这道题我是怎么想也做不出来,今天看了公布的答案,看到有学Java才7天都做出来了,真是佩服的5体投地。
当时我的解题思路一直没有,就是死板的认为是直接两个for循环就能实现输出结果,我当时的解题详细思路在:http://bbs.itheima.com/forum.php?mod=redirect&goto=findpost&ptid=48130&pid=310029
今天看了公布的答案,没有细看别人的代码,就是看了做题的思路,原来不是直接用两个for循环就能完成的,而是先把整个数字的完整图形装到一个二维数组里面,代码我没多看,思路有了我就自己敲敲看了。
实在是不想贴出来的,因为是看了别人的思路才能做出来的,真是惭愧呀,但是出了个问题,想向大家请教,也顺便把自己的理解说一,不会的可以参考下。
思路有了,具体要写代码,还是得找出具体规律来才行:
1、数字的图形用一个二维数组装刚好
2、肯定是按数字1、2、3这样顺序存的,只是具体存在数组的哪个位置呢?
a、看上图是从1开始的,先右移,然后是下移,然后是左移,然后是上移,就是这样4个方向循环的移动数组的下标来存放1、2、3、4、5、7这样的数字
b、按照右、下、左、上的顺序,每移动一个数字我就换一个方向移,如先存入数字1,然后按照顺序(右、下、左、上)来循环移动数组的位置来存放数字:
1)右移放2
2)下移放3
3)左移放4
4)上移放5
步骤3左移放4之后,接着按照右、下、左、上的顺序,左移后是上移,这时很明显上移的话,上边已经有数字1了,所以应该先左移一位,然后再尝试上移,如果发现上边的一位还是有值的话,依旧再左移一位再尝试上移,依此类推
注:步骤4是规律中的难点,其实步骤1、2、3也得像步骤4一样做判断,就是:
右移时得先判断右边一位是否有值,如果有则应该先再上移一位再尝试右移,依此类推
下移时得先判断下边一位是否有值,如果有则应该先再右移一位再尝试下移,依此类推
左移时得先判断左边一位是否有值,如果有则应该先再下移一位再尝试左移,依此类推
5)就是用上面的4个步骤一直循环,循环结束的条件是什么呢,我的判断条件是用输出的值如果等于最大值时就停止循环,如:求n的图形,那么数字的最大值是n×n。
6)循环的规律找出来了,关键是1的位置怎么确定,我的做法是:多画几个图来观察,有时候真得多看几个图才能看到规律,通过观察,发现1的位置的x与y轴的值是相等,如求n的图形,那么1在数组中的位置为:
int xScale = (n + 1) / 2 - 1; // 当前要输出的数字在X轴位置的初始化值
int yScale = xScale; // 当前要输出的数字在Y轴位置的初始化值
具体代码如下:- public static void main(String[] args) {
- drawCustomShape(3);
- }
- private static final int toRight = 1, toDown = 2, toLeft = 3, toUp = 4; // 数字的移动是顺序按右、下、左、向上循环转的
- /** 填充整形数组方法 */
- public static int[][] fillIntArray(int n) {
- if (n <= 0) throw new IllegalArgumentException("参数n必须是大于或等于1的整数");
- int[][] intArray = new int[n][n];
- int max = n * n; // 最大输出数,可用来做结束循环的条件
- int num = 1; // 当前要输出的数,默认从1开始
- int xScale = (n + 1) / 2 - 1; // 当前要输出的数字在X轴位置的初始化值
- int yScale = xScale; // 当前要输出的数字在Y轴位置的初始化值
- intArray[xScale][yScale] = num++; // 把1放到固定的位置
- int direction = toRight; // 数字的移动方向:数字从1开始,默认开始向右移,然后是向下、向左、向上
- while (num <= max) {
- switch (direction) {
- case toRight: // 向右,则把x轴坐标+1
- if (0 != intArray[xScale + 1][yScale]) { // 如果右边的元素有值了,说明不能向右移了,应该向上移
- direction = toUp;
- } else { // 如果右边的元素值为0,说明这个元素还没有被赋值,则可以右移
- intArray[++xScale][yScale] = num++;
- direction = toDown; // 右移后尝试向下移
- }
- break;
- case toDown: // 向下,则把y轴坐标+1
- if (0 != intArray[xScale][yScale + 1]) { // 如果下边的元素有值了,说明不能向下移了,应该向右移
- direction = toRight;
- } else { // 如果下边的元素值为0,说明这个元素还没有被赋值,则可以下移
- intArray[xScale][++yScale] = num++;
- direction = toLeft; // 下移后尝试向左移
- }
- break;
- case toLeft: // 向左,则把x轴坐标-1
- if (0 != intArray[xScale - 1][yScale]) { // 如果左边的元素有值了,说明不能向左移了,应该向下移
- direction = toDown;
- } else { // 如果左边的元素值为0,说明这个元素还没有被赋值,则可以左移
- intArray[--xScale][yScale] = num++;
- direction = toUp; // 左移后尝试向上移
- }
- break;
- case toUp: // 向上,则把y轴坐标-1
- if (0 != intArray[xScale][yScale - 1]) { // 如果上边的元素有值了,说明不能向上移了,应该向左移
- direction = toLeft;
- } else { // 如果上边的元素值为0,说明这个元素还没有被赋值,则可以上移
- intArray[xScale][--yScale] = num++;
- direction = toRight; // 上移后尝试向右移
- }
- break;
- }
- }
- return intArray;
- }
- /** 画自定义图形方法 */
- public static void drawCustomShape(int n) {
- if (n <= 0) throw new IllegalArgumentException("参数n必须是大于或等于1的整数");
- int starWidth = n + 2; // 星星的宽度
- for (int i = 1; i <= starWidth; i++) { // 控制输出的行数
- for (int j = 1; j <= starWidth; j++) { // 控制输出的列数
- if (j == starWidth) { // 如果是最后一列
- System.out.print("*\n");
- } else if ((i == 1 || i == starWidth || j == 1)) { // 如果是第一行、或最后一行、或第一列
- System.out.print("*\t");
- } else { // 其他情况
- int[][] result = fillIntArray(n);
- System.out.print(result[i-2][j-2] + "\t");
- }
- }
- }
- }
复制代码 我想不明白就是为什么我的结果是一个逆时针的图形。我是按intArray[xScale][yScale] 中第一个下标代xScale表x轴,第二个下标yScale代表y轴来填充数据的,比如n为2时,1在坐标intArray[0][0]的位置,接着是右移坐标并赋值为2(即intArray[0+1][0]=2),再接着是下移坐标并赋值为3(即intArray[1][0+1]=3)。。。这样的逻辑有错吗?
作者: 刘胜寒 时间: 2013-5-2 21:33
楼主啊...
你可以没走一步打印一下你的数组...
你就会发现错误了..
代码执行的时候跟你想的不一样...
自己在换个角度去研究这一题了
这一题用两个for 我个人认为不可能
我的使用一个外层for 里面嵌套了四个for循环
作者: 戴振良 时间: 2013-5-3 09:53
尹桥印 发表于 2013-5-2 21:16
楼主才是用了心的。顶
谢谢,所以真心求解答呀!
作者: 曹睿翔 时间: 2013-5-3 09:55
好久不见,来占个楼
作者: 李德全 时间: 2013-5-3 10:31
楼主辛苦了,其实逆时针就四个状态不断重复
作者: 黄茂霖 时间: 2013-5-3 11:12
代码:- package com.itheima;
- public class Test {
- /**
- * @param args
- */
- public static void main(String[] args) {
- // TODO Auto-generated method stub
- String[][] heilx = helix2DArray(5);
-
- for(int i=0;i<heilx.length;i++){
- for(int j=0;j<heilx.length;j++){
- System.out.print(heilx[i][j]+" ");
- }
- System.out.println();
- }
- }
-
- //定义一个螺旋二维数组
- public static String[][] helix2DArray(int size){
- //数组的大小就是数字的前后各加1
- int len = size + 2;
- String[][] heilx = new String[len][len];
- //遍历数组
- for(int x=0;x<len;x++){
- for(int y=0;y<len;y++){
- //凡是第一行和最后一行都是*号
- if(x==0||x==len-1){
- heilx[x][y] = "*\t";
- }
- //凡是第一列和最后一列都是*号
- if(y==0||y==len-1){
- heilx[x][y] = "*\t";
- }
- }
- }
- /*
- * * * *
- * 1 2 *
- * 4 3 *
- * * * *
-
- 找规律:如果 size是双数 则从 len/2-1开始,否则 len/2
- 循环size次,分别x和y的变化是: 第一次 y++ 第二次 x++ 第三次 y-- y-- 第四次 x-- x-- 以此类推
- 有点类似于 y: 1 34 789 131415
- x: 2 56 101112
- */
- if(size%2==0){
- heilxNumber(heilx, len/2-1, len/2-1, size);
- }else{
- heilxNumber(heilx, len/2, len/2, size);
- }
- return heilx;
- }
-
- private static void heilxNumber(String[][] heilx,int x,int y,int size){
-
- //定义让y循环多少次
- int a = 1;
- //定义让x循环多少次
- int b = 1;
- //定义一个判断y或x是该++还是该--
- int temp = 1;
- //存入螺旋数值
- int h = 1;
- heilx[x][y] = temp+"";
- while(temp<=size){
- for(int i=0;i<a;i++){
- if(temp%2==0){
- heilx[x][y] = h+"\t";
- h++;
- y--;
-
- }else{
- heilx[x][y] = h+"\t";
- h++;
- y++;
- }
- }
- if(temp<size){
- for(int j=0;j<b;j++){
- if(temp%2==0){
- heilx[x][y] = h+"\t";
- h++;
- x--;
-
- }else{
- heilx[x][y] = h+"\t";
- h++;
- x++;
- }
- }
- }
- a++;
- b++;
- temp++;
- }
- }
- }
复制代码 我的思路在程序中都写了,最近有点忙,没有修改!~你可以参考一下试着修改,由于做的急输入0的时候有点小问题。你调一下!~
作者: 戴振良 时间: 2013-5-3 13:10
李德全 发表于 2013-5-3 10:31
楼主辛苦了,其实逆时针就四个状态不断重复
我也知道是四方向不停的转,我还知道这道题可以有两种大变化8种微变化:
一、顺时
1、数字先右移
2、数字先下移
3、数字先左移
4、数字先上移
二、逆时
1、数字先右移
2、数字先下移
3、数字先左移
4、数字先上移
用我上面代码,假如用n=3,输出结果为:
* * * * *
* 7 6 5 *
* 8 1 4 *
* 9 2 3 *
* * * * *
可以看到我这个图是逆时,并且数字是先下移的,即1往下移放数字2,然后开始逆时针的转,这个转无非就是x轴加1或减1,y轴加1或减1,关键是我用顺时的思路来做题,做出了逆时的结果,不知道我思路代码里哪里想反了
作者: 丘凤光 时间: 2013-5-3 15:05
你们都用的for循环吗?
我觉得用while循环更简单呢,一个while循环里边嵌套四个上下左右方向的while循环,定义四个标签控制while循环的运行与停止 ,最外边一个while循环用输入的n控制
作者: 戴振良 时间: 2013-5-3 17:55
丘凤光 发表于 2013-5-3 15:05
你们都用的for循环吗?
我觉得用while循环更简单呢,一个while循环里边嵌套四个上下左右方向的while循环, ...
我用一个while循环就把螺旋状的数字都装好到数组里了,关键是我对这个数组的移动方向搞不清楚了,输出来是的数字图形是先向下移,并且逆时针转,还没想明白
作者: 陈圳 时间: 2013-5-3 21:13
- public class LineArray {
- public static void main(String[] args) {
- createArray(5,true);//true为顺时针输出,false为逆时针输入
- }
- public static void createArray(int n,boolean flag) {//加入一个标记,可以实现逆时针或者顺时针螺旋输出
- int index = 1;
- Direction dir;
- if(flag)
- dir= Direction.D;
- else dir= Direction.A;
- int[][] arr = new int[n][n];
- int row = n / 2, col = n / 2, temp = 0, step = 0;
- while (index != n * n) {
- if (step % 2 == 0 && step != (n - 1) * 2)
- temp++;
- for(int i=0;i<temp;i++){
- switch(dir){
- case D:arr[row][++col]=++index;break;
- case S:arr[++row][col]=++index;break;
- case A:arr[row][--col]=++index;break;
- case W:arr[--row][col]=++index;break;
- }
- if(i==temp-1)
- step++;
- }
- dir = dir.nextDirection(flag);
- for (int i = 0; i < arr.length; i++)
- System.out.println(Arrays.toString(arr[i]));
- System.out.println("----------------------------------------");
- }
- }
- }
- enum Direction {
- D, S, A, W;// 四个方向,螺旋数组的行走方向
- public Direction nextDirection(boolean flag) {// 循环四个方向
- if(flag){
- switch (this) {
- case D: return S;
- case S: return A;
- case A: return W;
- case W: return D;
- }
- } else {
- switch (this) {
- case A: return S;
- case S: return D;
- case D: return W;
- case W: return A;
- }
- }
- return null;
- }
- }
复制代码 突然来的思路,我用俩个for循环搞定了.螺旋数组
作者: cowthan 时间: 2013-5-3 22:06
其实前天我参加第二轮笔试就抽到这个题了,所以就没参加挑战,否则有作弊的嫌疑啊,其实想想也不难,装进一个二维数组是肯定的,打印二维数组时再添加星号就行了,至于二维数组怎么构造,我的思路是两个for循环:
1、始终是按四个方向走,且根据顺时针逆时针,下一个方向始终是定的
2、每次遍历所构造的元素个数:假如n=4,那就是4,3,3,2,2,所以就根据这个来决定第二层循环的次数就行,除了第一次,每两次循环都减一
3、循环体内根据当前方向确定改变横坐标还是纵坐标
4、循环结束的时候改变方向,计算下一轮的遍历点数,用个计数器就行了
5、唯一需要注意的问题就是每次第二轮循环要结束的时候把当前坐标移动到下一个要影响的位置
作者: Just_Only 时间: 2013-5-4 09:23
哇 都好犀利的啊
作者: 花开花落总相似 时间: 2013-5-4 14:10
好吧 我都没想过这个问题 ,现在在用数组的思想 思考一下 看我能不能写出来
作者: 戴振良 时间: 2013-5-5 16:48
shenqi 发表于 2013-5-3 11:12
代码:我的思路在程序中都写了,最近有点忙,没有修改!~你可以参考一下试着修改,由于做的急输入0的时候有 ...
你的代码很简洁,学习了,谢谢!
您的代码还可以优化,对于算1在二维数组中的位置,其实一句代码就够了:(size+1)/2
heilxNumber方法中,有些变量是重复的,变量a与变量b可以不要,我加以修改后30几行就搞定了,详见:http://bbs.itheima.com/forum.php ... 8731&pid=314277
作者: 戴振良 时间: 2013-5-5 16:52
陈圳 发表于 2013-5-3 21:13
突然来的思路,我用俩个for循环搞定了.螺旋数组
你的枚举用的是好好啊,我怎么就没想到呢,这四个方向用枚举真是太合适了,学习了,谢谢!!不过你的代码是有问题的,您的结果都是从0开始输出的,输入1没结果,输入双数(如2、4、6会报错)。我加以修改,实现了8种情况的输出,详见:http://bbs.itheima.com/thread-48731-1-1.html
作者: 戴振良 时间: 2013-5-5 17:09
大家都答非所问了,其实我的代码并没有问题,能正确的输出螺旋状的图形,只不过是逆时针的而已,如下图:
* * * * *
* 7 6 5 *
* 8 1 4 *
* 9 2 3 *
* * * * *
我的问题描述的应该挺清楚的吧:“我是按intArray[xScale][yScale] 中第一个下标代xScale表x轴,第二个下标yScale代表y轴来填充数据的,比如n为2时,1在坐标intArray[0][0]的位置,接着是右移坐标并赋值为2(即intArray[0+1][0]=2)”,再换句话问是:“对于int[][] intArray = new int[j]; 这代码中,第一个下标i到底是代表x轴还是y轴,第二个下标j代表x轴还是y轴?” 没人回答,我只能自己研究了,毕老师说的对,画图分析法确实很有助于问题的分析,下面我就画图分析,希望对此不懂的大家也能有帮助:
上面是一个大小3乘3的表格,用代码表示即:int[][] intArray = new [3][3]; ,对于我的问题,我就是想问intArray数组中第1个下标和第2个下标,哪个相当中上图中的X轴,哪个相当于上图中的Y轴?或许大家真的不明白我在说什么吧,打个比方吧:
上图中有红色点的方格的坐标是什么?可以是1 2 ,也可以是 2 1吧!
1、如果是1 2,那么1是第一个下标,而且是Y轴方向的1,如果坐标为1 2,说明第一个下标代表Y轴
2、如果是2 1,那么2是第一个下标,而且是X轴方向的2,如果坐标为2 1,说明第一个下标代表X轴
最后经过代码的测试后我才确定红色方格的坐标是1 2,换成代码也就是说红色位置的方格它代表的元素为:intArray[1][2],也就是说数组中的第1个下标代表的是Y轴,数组中的第二个下标代表的是X轴,到此就可以解释我的代码为什么输出的是逆时针的螺旋状了。
因此在这里给大家纠正一个观点:
以前我们做*号的输出练习时,习惯用变量x来表示行数,用y来表示列数,但这种想法不能用到数组中,因为数组中的y轴是表示行数的,x轴是表示列数的。
从我上面的代表可以看到我是把x、y轴搞反了(intArray[xScale][yScale])这里我把第1个下标当x轴了,把第2个下标当y轴了,所以结果也就反了。希望对大家有帮助!也希望大家以后在回答问题的时候看清楚问题,先回答主要问题,别答非所问。
作者: 陈圳 时间: 2013-5-5 17:23
本帖最后由 陈圳 于 2013-5-5 17:32 编辑
戴振良 发表于 2013-5-5 16:52
你的枚举用的是好好啊,我怎么就没想到呢,这四个方向用枚举真是太合适了,学习了,谢谢!!不过你的代码 ...
双数的后面我我修复了一下.就是初始角标的问题.
if(n%2==0)//判断是奇数二维数组还是偶数
row=col=n/2-1;
else row=col=n/2;
arr[row][col]=1;
把初始的改成这个,程序就一切正常了 .因为我做题比较关注思路,思路好了.其他的细节完善就已经很容易了.
至于上面的/2不好用位移.因为运算优先级的问题.我有个问题不懂.什么叫从0开始输出,输入1没有结果?
我请教一点,关于一个问题,或是思路受阻,花长时间去钻是否值得?我最初知道螺旋数组这题时,花了二三个小时解出来了,但当时算法低劣.
程序阅读性太差.甚至于我二次重写,都理不清当时的思路.这题关于枚举的思路是来源于毫不相干的题.
http://bbs.itheima.com/forum.php?mod=viewthread&tid=48446,这题我用枚举解出来.回想起来螺旋数组,才开始有的思路,
http://bbs.itheima.com/thread-48560-1-1.html,这是思路有了后,代码一步一步改的.我想了解一些前辈您的解题思考方式.您是遇到一道起初没有思路的题,如何
从构思到解题,您在做每道题时都会分析这么多吗?
作者: 戴振良 时间: 2013-5-5 20:02
陈圳 发表于 2013-5-5 17:23
双数的后面我我修复了一下.就是初始角标的问题.
if(n%2==0)//判断是奇数二维数组还是偶数
...
0开始输出就是说,螺旋状的开始都是0开始的,如求2的螺旋状,应该输出1、2、3、4,而你的是0、2、3、4
输入1没结果是说求n=1的螺旋状时没看到输出结果
这个我觉得看你自己的兴趣,如果有兴趣就钻研,兴趣是最好的老师,如果没兴趣,单是为了做题而做题,那就没有意义了,当然了,如果没有思路,那就从最简单的开始,能完成多少是多少,实在没思路也不要去纠结,这就是我们需要学习的时候,因为我们不懂才需要学习,否则我们学习是干嘛的,你看我开始的答题,比你还没思路呢:http://bbs.itheima.com/forum.php ... 8130&pid=310029,可后来学习了你们的思路就有头绪了。我觉得做题分析是很有必要的,分析的好,可以少写很多代码,写起来也轻松。关键一点还要做笔记,不然以后几天后就该忘了。
作者: 黄玉昆 时间: 2013-5-5 22:40
如果问题解决了,请将帖子分类改为“已解决”,谢谢
欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) |
黑马程序员IT技术论坛 X3.2 |