- 浏览: 28200 次
- 性别:
- 来自: 深圳
最新评论
-
hpzxyj:
${riskLevelMap[fn:length(riskle ...
如何用jstl标签将number类型转换为String类型? -
ilove2009:
<div class="quote_title ...
如何用jstl标签将number类型转换为String类型? -
pouyang:
我正好遇到这样的情况<c:out value=& ...
如何用jstl标签将number类型转换为String类型? -
sav2005:
确实很强啊~~佩服
从一道面试题想到的 -
bill600:
看看同道的讨论 觉着很有意思 我也弄一个试试
思路很简单
把 ...
从一道面试题想到的
最近在 javaeye 上看到一道面试题。
要求打印出:
int i = 5;
1 2 3 4 5
16 17 18 19 6
15 24 25 20 7
14 23 22 21 8
13 12 11 10 9
int i = 6;
1 2 3 4 5 6
20 21 22 23 24 7
19 32 33 34 25 8
18 31 36 35 26 9
17 30 29 28 27 10
16 15 14 13 12 11
从发贴人的意图来看,这应该是一道 java 面试题( http://www.iteye.com/topic/545378?page=1 );
看到很多热心的网友给出各种各样的答案,其中有 java 、 c 、 ruby 的。看到很多人这么有兴趣,我也给出了自己的答案。
不过我还想到另外两个问题:
1 、什么语言的题目?
2 、考官的目的?
如果是 c 语言,那么第二个问题一定是考查面试者的算法。如果这是一道 java 面试题,那就很难说了。第一,跟考官的水平和思维很大的关系;
第二,确实考查面试者的算法能力;
第三,考查的是面向对象分析和设计的能力。
本人更倾向于第三种,为什么呢?因为主要是 java 本身是面向对象的语言;其次算法已经很成熟了,平时工作可参考资料就可以实现。
很多网友给出的答案大多都是基于面向过程的算法实现。如果这些实现放在其他面向过程的语言中可能非常适合,如果在 java 中就不合适了。 java 是一个面向对象的语言,面向对象的出现无非是为了解决面向过程中出现的一些缺点:可维护性、可扩展性、代码可复用性差等。所以在 java 语言中应该是考查我们的面向对象分析和设计的能力,而不是算法实现 !
如果我是考官,那么让我看一个人的算法实现是很痛苦的事情。首先我没有那么多脑力计算出这个算法实现是否正确,其次让我把代码一行一行敲入计算机,那肯定没有这个功夫。
所以,大家以后碰到这类题目不要一上来就冥思苦想,然后写出让人难于看懂的算法实现。当然也有例外,那就是有些考官本身水平和思维有限,就是想看到你是否有代码。
3 年前我在面试中也碰到类似的题目,我的做法仅仅是说明为了实现这个功能我有那些类和关键方法。结果很不利,所以这个跟考官的水平和思维有很大的关系。
像这道面试题,我第一次看到就联想到这个是一个贪吃蛇游戏的简化版。如果联想不到呢,那就没辙啦。那也不会。
首先,我们从上面可以看出这是一个矩阵,它的高度和宽度是一样的,矩阵中有一些格子存放数字,这个格子就是一个点。
其次,这些数字的增长是有规律的,即顺时针而且不断内旋。
所以我们可以得到两个关键的类:点( Point )和方向( Direction )。既然存在方向这个概念,那么一定存在它的主体,比如车、火箭等,这里我们就用蛇( Snake )吧。
既然有了类,那么他们有那些属性呢?
想到点我们必定也能想到坐标中用 x 、 y 变量来表示。另外跟我们问题域有关的是在这个点上要记录一些信息。所以点有以下定义:
class Point {
private int x ;
private int y ;
private String val ;
}
方向这个概念比较抽象,在我们要研究的问题域中方向有向左、向右、向下、向上,而且有一定的范围。所以在给定范围的方向上,类 Direction 应该知道一个点是否出界,能够计算本方向上下一个点是什么。另外从题目上看,方向的变化规律是顺时针的,所以类 Direction 应该也能够知道它下一个方向是什么。所以方向有以下定义:
abstract class Direction {
int size ;
abstract public Point nextPoint(Point p); // 下个点
abstract public Direction next(Point p); // 下一个方向
abstract public boolean isOut(Point point); // 是否在本方向上出界
}
类 Direction 有四个子类,分别是右、左、下、上。这四个子类通过 next 方法返回另一个 Direction 构成类一个顺时针。
再来看看另外一个核心类 Snake ,它有那些属性呢?这个 Snake 会在一个方向和指定范围内进行游走,碰到壁什么的会掉头,不走已经走过的点。所以它能记住自己走过的点,并且在那里做些标记。最后还会打印出自己走过的路径。
public class Snake {
int size ;
Direction direct ;
Point next ;
Map<String, Point> map = new HashMap<String, Point>(); // 记录走过的点
public Snake( int size, Direction direct, Point point) {
this . size = size;
this . direct = direct;
next = point;
map .put(key( next ), next );
}
// 游走
public void run() {
for ( int i = 2; i < size * size + 1; i++) {
Point tempNext = direct .nextPoint( next );
// 如果在一个方向上出界或者已经走过的点要掉头
if ( direct .isOut(tempNext) || containsPoint(tempNext)) {
direct = direct .next(tempNext);
next = direct .nextPoint( next );
} else {
next = tempNext;
}
next .setVal(String. valueOf (i));
map .put(key( next ), next ); // 记录走过的点
}
}
// 打印走过的点
public void print() {
for ( int y = 1; y < size + 1; y++) {
for ( int x = 1; x < size + 1; x++) {
String temp = " "
+ (containsPoint( new Point(x, y)) ? map .get(key(x, y))
.getVal() : " " );
System. out .print(temp.substring(temp.length() - 5));
}
System. out .println( "" );
}
}
private String key(Point point) {
return key(point.getX(), point.getY());
}
private String key( int x, int y) {
String xVal = "00000" + x;
String yVal = "00000" + y;
return xVal.substring(xVal.length() - 3)
+ yVal.substring(yVal.length() - 3);
}
private boolean containsPoint(Point point) {
return map .containsKey(key(point));
}
public static void main(String[] args) {
// 指定范围、起始点、起始方向
Snake snake = new Snake(6, new Right(6), new Point(1, 1, "1" ));
snake.run();
snake.print();
}
}
到此,如果只给出 Snake 的属性和关键方法( run,print )的实现就可以表达你的设计意图了,其他的只不过是实现细节而已。
为什么这么实现,首先我觉得是自然,至于有什么好处当时还没有想过。看看这句就非常达意:
Snake snake = new Snake(6, new Right(6), new Point(1, 1, "1" ));
对蛇指定范围、起始方向、起始点才能让蛇开始游走。从这里我们也可以知道,对于蛇来讲只要你告诉它这几个输入,它就知道怎么做了,其他的你不用管。
这样设计有什么好处呢?从题目来看,用户只不过想在一个矩阵上打印一些数字序列。我们知道用户的想法最不可靠了。更多的想法如下:
1 、还可以从四个角开始顺时针内旋;
2 、分别可以从四个角逆时针内旋;
3 、还可以这样:
1 2 3 4 5 6
12 11 10 9 8 7
13 14 15 16 17 18
24 23 22 21 20 19
25 26 27 28 29 30
36 35 34 33 32 31
4 、在单数的序列上用红色字体:
1 2 3 4 5 6
20 21 22 23 24 7
19 32 33 34 25 8
18 31 36 35 26 9
17 30 29 28 27 10
16 15 14 13 12 11 ;
5 、 ...........
其中:
关于第一个想法,我们一开始就认为是顺时针内旋的,只要正确的初始化起始点和起始方向就可以轻松满足用户需求:
Snake snake = new Snake(6, new Down(6), new Point(6,1, "1" ));
Snake snake = new Snake(6, new Left(6), new Point(6,6, "1" ));
Snake snake = new Snake(6, new Up(6), new Point(1, 6, "1" ));
关于第二、三个想法,因为我们有 Direction 为抽象类,根据需要重写子类即可。
关于第四个想法,我想这个工作应该由蛇来做,你(蛇)到那个点上爱干嘛就干嘛,不关方向什么事。
所以好处是:
1 、第一、二、三个需求的变化是通过继承和扩展而不是修改来达到增加功能的目的,最大限度地保护 Snake 类的代码不受冲击。
2 、现在我们有很多各种各样“走法”的功能了,现在有点点变化就是想给数字加上颜色。我们不可能针对每种走法都进行修改吧。因为这个职责很自然地分配给了 Snake ,所以我们就只要修改 Snake 即可,同样其他代码得到保护。
下面是 Direction 子类实现示例:
class Right extends Direction {
public Right( int size) {
this . size = size;
}
public Direction next(Point point) {
return new Down( size );
}
public Point nextPoint(Point p) {
return new Point(p.getX() + 1, p.getY());
}
public boolean isOut(Point point) {
return point.getX() > size ;
}
}
class Down extends Direction {
public Down( int size) {
this . size = size;
}
public Direction next(Point point) {
return new Left( size );
}
public Point nextPoint(Point p) {
return new Point(p.getX(), p.getY() + 1);
}
public boolean isOut(Point point) {
return point.getY() > size ;
}
}
class Left extends Direction {
public Left( int size) {
this . size = size;
}
public Direction next(Point point) {
return new Up( size );
}
public Point nextPoint(Point p) {
return new Point(p.getX() - 1, p.getY());
}
public
boolean
</spa
评论
思路很简单
把数据按照螺旋矩阵的路线装进数组
然后再打印出来
public class PrintSquare {
private int[][] getArray(int n){
if(n>0){
int[][] array=new int[n][n];
int x=0;
int y=0;
int xStart=0;
int xEnd=n-1;
int yStart=0;
int yEnd=n-1;
for(int i=1;i<=n*n;i++){
array[y][x]=i;
if(y==yStart&&x<xEnd){//横向前进
x++;
if(x==xEnd){
yStart++;
}
}
else if(y<yEnd&&x==xEnd){//纵向前进
y++;
if(y==yEnd){
xEnd--;
}
}
else if(y==yEnd&&x>xStart){//横向倒退
x--;
if(x==xStart){
yEnd--;
}
}
else if(y>yStart&&x==xStart){//纵向倒退
y--;
if(y==yStart){
xStart++;
}
}
}
return array;
}
return null;
}
public void printSquare(int n){
if(n>0){
int[][] arr=getArray(n);
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
System.out.print(arr[i][j]+"\t");
}
System.out.println();
}
}
}
public static void main(String[] args) {
PrintSquare test=new PrintSquare();
test.printSquare(5);
System.out.println();
test.printSquare(6);
}
}
但如果像数据库里的select,每天都要进行不计其数的操作,这些东西可以抛开了. 最高效,最有效的办法,才是上策. 而最高效,往往和巧妙的算法相关.
虽然这个题,偏第一种--花瓶,但我不支持楼主对算法的一些褒贬.
藐予小子,仅抒己见.
public class Calc {
public static void main(String[] args){
for(int i=1;i<8;i++){
calc(i);
System.out.println("---------------------------" +
"---------------------------");
}
}
public static void calc(int border){
//setup border here
int end = border;
int start = 1;
int seq = 1;
int[] c;
int[] n = {start, start};
int[][] data = new int[end][end];
data[0][0] = seq++;
do{
c = n;
n = next(c, start, end);
if(n[0] == start && n[1] == start){
start++;
end--;
n[0] = start;
n[1] = start;
if(start == end){
data[n[0]-1][n[1]-1] = seq++;
break;
}else if(start>end){
break;
}
}
data[n[0]-1][n[1]-1] = seq++;
}while(true);
for(int i=0;i<data.length;i++){
for(int j=0;j<data[i].length;j++){
System.out.print(data[j][i] + "\t");
}
System.out.print("\n");
}
}
private static int[] next(int[] current, int start, int end){
int x = current[0];
int y = current[1];
if(x == end && y != end){
y++;
}else if(x > start && y == end){
x--;
}else if(x == start && y > start){
y--;
}else if(x < end && y == start){
x++;
}else{
x = start;
y = end;
}
return new int[]{x, y};
}
}
标题: 您的帖子被JavaEye会员集体投票评为新手帖
正文:
您的帖子:从一道面试题想到的 被JavaEye用户投票评为新手帖帖,积分-10分。
发贴前请仔细阅读 JavaEye版规和提问的智慧,如有异议,可以到JavaEye站务圈子申诉。
如果您只是急切的想找人解答你的问题,而不是为了讨论技术的话,请移步到JavaEye问答频道,在问答频道您可以用积分悬赏解答,并且不受论坛发贴规则的制约,谢谢!
这是系统的自动通知,无需回复
/**
* Created on 2009-12-29
* <p>Description: [描述该类概要功能介绍]</p>
*/
public class Game
{
private static final String BLANK = " ";
public static void main(String[] args)
{
// 打印的结果可以理解为一个正方形,参数相当于正方形的边长
int[][] target = createArray(6);
printArray(target);
}
/**
* <p>Discription:[参数相当于正方形的边长]</p>
* @param iLength
* @return
*/
private static int[][] createArray(int iLength)
{
int[][] target = new int[iLength][iLength];
int iCircle = (iLength % 2 == 0) ? iLength/2 : iLength/2 + 1;
int a1 = 1;
for (int k = 0; k < iCircle; k++)
{
int a2 = a1 + iLength-1;
int a3 = a2 + iLength-1;
int a4 = a3 + iLength-1;
int a4End = a4 + iLength-2;
int iColumn = k + iLength;
// 向右移动
for (int m = k, n = k; n < iColumn; n++)
{
if (n == k) {
target[m][n] = a1;
} else {
target[m][n] = target[m][n - 1] + 1;
}
}
// 向下移动
for (int m = k, n = iColumn -1; m < iColumn; m++)
{
if (m == k) {
target[m][n] = a2;
} else {
target[m][n] = target[m - 1][n] + 1;
}
}
// 向左移动
for (int m = iColumn -1, n = iColumn -1; n >= k; n--)
{
if (n == iColumn - 1) {
target[m][n] = a3;
} else {
target[m][n] = target[m][n + 1] + 1;
}
}
// 向上移动,移动到起始位置的下方的点
for (int m = iColumn -1, n = k; m > k; m--)
{
if (m == iColumn - 1) {
target[m][n] = a4;
} else {
target[m][n] = target[m + 1][n] + 1;
}
}
a1 = a4End + 1;
iLength -= 2;
}
return target;
}
private static void printArray(int[][] target)
{
int iLength = target.length;
for (int i = 0; i < iLength; i++)
{
for (int j = 0; j < target[i].length; j++)
{
String str = String.valueOf(target[i][j]);
if (str.length() == 1) str = BLANK + str;
System.out.print(str + BLANK);
if ((j+1)%iLength == 0) System.out.println();
}
}
}
}
考设计会出这样的题?真不知道是谁脑子坏了
简单问题复杂化确实是不好的习惯, 不过也因情况而异。
这种问题确实需要做些设计的, 也许客户需求变化了呢?不是螺旋矩阵而是贪吃蛇呢?
如果是一个网络游戏, 物体行走路线算法呢?Point, Direction这种封装还是有必要的。
当然这种问题在面试时回答要看对方是什么样的公司, 考察什么内容, 如果C程序员职位, 做底层开发的, 自然不用这么封装, 如果是Java程序员, 做游戏或者产品的, 不封装一下才是找抽呢。
说得好,需求变化了怎么办.需求变成不是螺旋矩阵也不是贪吃蛇,而是三维空间多方向同时的旋转,且几个旋转方向互相影响,我倒想看看这段所谓OO的代码要改成啥子样子.
你举的网络游戏例子更不合适,我就是做网络游戏服务端的,那种效率优先的地方最直接的行走路线算法都嫌慢(防外挂,路点必须都做检查),要是到处都"封装"一下,多"完美"啊,只是一台服务器只上得到100人,这可不怪我了.
OO不是银弹更不是一切,KISS才是程序员应该树立的正确的世界观.感觉JAVA程序员很多走进了OO的怪圈.为了OO而OO,真正重要的是什么?是设计,是了解你要做的东西的业务逻辑而做出合理的能适应修改的设计.
别天真的以为封了几个类就灵活了
考设计会出这样的题?真不知道是谁脑子坏了
简单问题复杂化确实是不好的习惯, 不过也因情况而异。
这种问题确实需要做些设计的, 也许客户需求变化了呢?不是螺旋矩阵而是贪吃蛇呢?
如果是一个网络游戏, 物体行走路线算法呢?Point, Direction这种封装还是有必要的。
当然这种问题在面试时回答要看对方是什么样的公司, 考察什么内容, 如果C程序员职位, 做底层开发的, 自然不用这么封装, 如果是Java程序员, 做游戏或者产品的, 不封装一下才是找抽呢。
不过你也提供了对这人问题的另一种解法,也不错,不过这样很容易让面试的人认为你过于学术派。有点生搬硬套,过份OOP的样子发表评论
相关推荐
这篇博客文章“从一道Neusoft题中想到的Java日志API”可能涉及了在面试或实际项目中遇到的日志相关问题,以及如何通过Java的日志API来解决这些问题。 Java的日志API主要包括以下几个主要库: 1. **java.util....
最近群里有人发了下面这题: 实现一个函数,运算结果可以满足如下预期结果: add(1)(2) // 3 add(1, 2, 3)(10) // 16 add(1)(2)(3)(4)(5) // 15 对于一个好奇的切图仔来说,忍不住动手尝试了一下,看到题目首先想到...
面试题通过一道题考一个专类方面的能力。说起Java,人们首先想到的是Java编程语言,然而事实上,Java是一种技术,它由4个方面组成:Java编程语言、Java类文件格式、Java虚拟机和Java应用程序接口(Java API)。从...
面试题通过一道题考一个专类方面的能力。说起Java,人们首先想到的是Java编程语言,然而事实上,Java是一种技术,它由4个方面组成:Java编程语言、Java类文件格式、Java虚拟机和Java应用程序接口(Java API)。从...
本文讨论了一道常见的笔试面试题目:从10亿个浮点数中找出最大的1万个。这个问题看似简单,但实际上却具有很高的难度,因为处理如此大量的数据需要考虑内存和性能问题。 首先,我们可以想到的方法是建立一个数组把1...
leetcode面试-腾讯专题做题记录 LeetCode腾讯专题地址, 小目标 每天至少一道算法题 每天更新README学习markdown写法(不会真的有人不会markdown吧,不会吧,不会吧) 先这样,有什么想到的再说 1月20日 正式开始,...
阿里巴巴面试题leetcode CharSimpleAlgorithm 字符串算法 WordConversion 该类是用于改变字母的大小写,使一...ps:该类的实际意义没有想到,是我在浏览LeetCode评论区的时候看到的一道阿里巴巴面试题,所以就实现了一下
链表反转是常见的面试题,主要考察程序员对基本数据结构的操作能力和逻辑思维能力。这里我们将详细讨论两种链表反转的方法。 首先,我们来看最常用的一种方法,即迭代法。迭代法的基本思想是使用三个指针pre、cur和...
PS:据说这是迅雷的一道面试题,不过问题本身具有很强的真实性,所以本文打算按照真实场景来考虑,而不局限于面试题的理想环境。首先,我们用一张用户积分表user_score来保存用户的积分信息。表结构:示例数据:下面...
这是一道面试题,要求是这样的: 只使用一个for循环输出下面图形: 如果可以使用2个for(即嵌套循环的话),那这题就很简单了。 但只能用一个for,这可把我想得,想到面试都结束了没想出来。 后来使用String对象,可以...
有一道面试题: Python中如何处理中文问题,能想到的就是以下几方面来规避: 1. 首行添加 # coding = utf-8 # coding = utf-8 # 或者 # -*- coding:utf-8 -*- 2. 字符串前添加u >>> s = u'中文' >>> print(s) 中文 3...
去某公司(公司名不说了,人这套题说不定还要用)面试,现场30分钟做了一套题,其中有一道是这样的: 要求用js写一个函数,对传入的形如下网址字符串,返回对应的对象。 如: 若传入字符串a=’?name=zhiyelee&blog=...
8. 这是一个事件逻辑顺序的排列题,描述了一个产品从出现问题到重新获得市场认可的过程,涉及产品质量、客户反馈、企业改正、采用新方法和重返市场等步骤。 9. 这是一道诗词对联题,需要找出与"江流天地外,山色...
春节的时候去面试了一家公司,笔试题里面有一道是使用简单的代码实现线程的‘死锁’,当时没有想到这道题考的是Synchronized关键字,于是自己定义了两个资源模拟了一下。后面想想肠子都悔青了,于是自己在电脑上...