面试题

五柳--先生 2012-10-22 02:39:36
这是一题笔试题,题目是实现一个交通灯管理系统,就是我们路口的红绿灯(不考虑黄灯),每辆车通过路口时间为1S,随机生成车辆和红绿灯交换时间间隔自定,可以设置,不需要做界面的实现。



看题目,我们先要理清思路再去写,首先想清楚交通灯是什么一个情况的:

有直行的,左拐的,右拐的。

1.所有车同时直行,肯定是要撞上了,只能东西方向直行,或者是南北方向直行。

2.所有路口的车辆同时左拐是可以的

3.所有车辆同时右拐也是可以的。



问题理清楚了,我们就开始试着去将它抽象出一个程序模型出来:

1:路可以作为一个对象,路上对应的灯可以作为是路的一个变量(最好是bool型的,有true or false两种情况),路上的车也是路德变量,红灯的时候增加,绿灯的时候减少。

2:这样的话我们只需要考虑四条线路,然后定义路上的车辆和红绿灯,让他们按照规律进行变化

3:这样我们就可以将问题抽象为 四个路线对象(东西,南北,所有路口左转的,所有路口右转的)

4:路对象的红绿灯呢在 true false之间跳转。

5:路对象的车变量在红灯的时候增多,绿灯的时候减少。



这样我们的模型就抽象出来了,我们只需要考虑四条路线随时间变化的情况。



模型抽象出来了,接下来我们再看一下实现这个模型需要用到哪些技术,和需要处理的技术细节。

1.四条路的状态时同时在变的(所以我们这里肯定是要用到多线程,没有二话的)

2.四条路线,在同一时间点只能有一个是绿灯,其他必须是红灯(否则撞车了)

a:要实现这个,我们把一个时间分为四份,每条路线占据的绿灯时间为四分之一,

b:这些路线的红绿灯按照一定的规律亮绿灯(绿红红红 红绿红红 红红绿红 红红红绿)这样的一个顺序一直变化下去(怎么实现呢,我们可以理想化一点,在我的程序里,我先定义一个路线,在绿灯时间过后定义第二个路线,这样一直到第四个路线)这样实现后,他们会按照指定的时间一直有规律的变化下去

3.当路口是红灯的时候,我们路上的车会越来越多,对车辆进行增加操作,当路口是绿灯的话,我们的车辆就进行减少,一直这样进行下去。

4.需要时间可以设置,那太简单了,我们只需要给它增加一个 settime()函数就OK拉。



下面是我实现的程序,没花时间在细节上,只是大概给它把功能实现了一下,肯定会有很多问题,也希望高手能贴出自己的解法,不吝赐教(当然这题目是源于张孝祥老师的一个视频,因为一些原因,我没有办法看那个视频,所以自己记下了题目,写了下,希望不会有板砖),也欢迎大家指正出里面的不足之处(毕竟我刚刚接触java),非常感谢了。

package Lamp;

/**
交通灯
*/
class road implements Runnable
{
int carnum = 0 ;
int time; //随机生成车辆的时间间隔
Lamp la; //监视灯的状态

road(int time,String name,Boolean state,int lamptime)
{
this.time = time;//初始化红绿灯时间
la = new Lamp(name,state,lamptime);
new Thread(la).start(); //启动灯监控线程
}

/*生成车辆时间间隔设置*/
void settime(int time)
{
this.time = time;
}

@Override
public void run()
{
while(true)
{
/*如果是绿灯,让车辆减少,否则,增加*/
if(la.state)
{
try
{
Thread.sleep(1000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
if(carnum>0)
carnum--;
System.out.println("name: "+la.name+" state: "+la.state+" num: "+carnum);
}
else
{
try
{
Thread.sleep(1000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
carnum++;
System.out.println("name: "+la.name+" state: "+la.state+" num: "+carnum);
}
}
}

}

class Lamp implements Runnable
{
String name;
Boolean state = true;
int time;

Lamp(String name,Boolean state,int time)
{
this.name = name;
this.state = state;
this.time = time;
}

/*红绿灯时间间隔设置*/
void settime(int time)
{
this.time = time;
}

@Override
public void run()
{
// TODO Auto-generated method stub
while(true)
{
if(state)
{
try {
Thread.sleep(this.time*1000); //绿灯亮 time时间
}
catch (InterruptedException e)
{
e.printStackTrace();
}
state = false;
}
else
{
try
{
Thread.sleep(3*(this.time*1000));//红灯亮 3*time时间
}
catch (InterruptedException e)
{
e.printStackTrace();
}
state = true;
}
}
}
}

/*
* 根据红绿灯常识,我们可以知道,南北向路口红绿灯是一个颜色的,所有东西向路口红绿灯是一个颜色的
* 所有路口左转(右转)红绿灯是一个颜色的(所有路口车辆左转(右转),不会有相交的)
* 四种灯在同一个时间,只能有一个是绿灯,其他必须为红灯,一次循环。
*/
public class Lampcontrol
{
public static void main(String[] args) throws InterruptedException
{
/*让第一个路口启动*/
road r1 = new road(1,"r1",true,10);
new Thread(r1).start();

Thread.sleep(r1.la.time*1000);

/*让第二个路口启动*/
road r2 = new road(1,"r2",true,10);
new Thread(r2).start();

Thread.sleep(r1.la.time*1000);

/*让第三个路口启动*/
road r3 = new road(1,"r3",true,10);
new Thread(r3).start();

Thread.sleep(r1.la.time*1000);

/*让第四个路口启动*/
road r4 = new road(1,"r4",true,10);
new Thread(r4).start();
}
}


下面是我的运行结果,因为处理过于简单,我需要做一下说明:
(为了容易看清,我把红绿灯时间改成了 3S ,方便我们看清)
name: r2 state: false num: 31
name: r3 state: true num: 30
name: r1 state: false num: 34
name: r4 state: false num: 31
name: r2 state: false num: 32
name: r3 state: false num: 29
name: r1 state: false num: 35
name: r4 state: true num: 32
name: r2 state: false num: 33
name: r3 state: false num: 30
name: r4 state: true num: 31
name: r1 state: false num: 36
name: r2 state: false num: 34
name: r3 state: false num: 31
name: r4 state: true num: 30
name: r1 state: false num: 37
name: r2 state: false num: 35
name: r3 state: false num: 32
name: r4 state: false num: 29
name: r1 state: true num: 38
name: r2 state: false num: 36
name: r3 state: false num: 33
name: r4 state: false num: 30
name: r1 state: true num: 37
name: r2 state: false num: 37
name: r3 state: false num: 34
name: r4 state: false num: 31
name: r1 state: true num: 36
name: r2 state: true num: 38
name: r3 state: false num: 35
name: r4 state: false num: 32
name: r1 state: false num: 35
name: r2 state: true num: 37
name: r3 state: false num: 36
name: r4 state: false num: 33
name: r1 state: false num: 36
name: r2 state: true num: 36
name: r3 state: false num: 37
name: r4 state: false num: 34
name: r1 state: false num: 37
name: r1 state: false num: 38
name: r2 state: false num: 35
name: r3 state: true num: 38
name: r4 state: false num: 35
name: r1 state: false num: 39
name: r4 state: false num: 36
name: r2 state: false num: 36
name: r3 state: true num: 37
name: r1 state: false num: 40
name: r4 state: false num: 37
name: r2 state: false num: 37

我们可以在结果里看一下,四个路口始终保持只有一个为true,true的时候车辆是减少的(每秒一辆),false的时候是增加的(每秒一辆)。
当然我们也可以看到我的数据肯定是有问题的,因为这样很快马路就会堵死了,所以这应该相当于现实生活中的堵车了,哈哈,所以理论时间是产生车辆需要小于等于通行车辆的1/4就是最佳状态哦,呵呵
...全文
108 3 打赏 收藏 转发到动态 举报
写回复
用AI写文章
3 条回复
切换为时间正序
请发表友善的回复…
发表回复
五柳--先生 2012-10-23
  • 打赏
  • 举报
回复
1:画图,这一步至关重要,我因为没画图,脑子想当然,导致了致命错误(画图的话我们可以看到:右转车辆和所有其他路线的车辆是都不会相撞的,所以他可以一直是绿灯,常识呀,哎。其次是左转灯,左转灯会干扰到对面开过来的车,所以当有车辆左转的时候,对面的车必须是红灯,而同方向的车,可以是绿灯)
上面对图的分析,我们就可以看出来,之前的程序可以说上来就错掉了。

2:实现方法
我这里的刚刚开始撕路就错了,就不贴了,我的思路,见之前的那一篇博客。
张老师解法:
a:定义12个 灯对象(其中4个右拐的,一直为true。其他的依次变换)。汗颜呐,当初我以为四个就够了
b:剩下的8个灯对象(分成四组,毫无疑问左拐方向的灯和右边车道的灯为一组,他们状态相同,并且同一时间,只有一组灯可以是绿灯)
c:在灯对象中lamp中用一个变量来保存下一个需要变绿的灯(这种思想面向对象,风险可以控制,我的方法是自己在程序中控制它的顺序,比较麻烦,而且容易出错,思想上其实是面向过程多一点,汗颜)
d:定义12条路线(东西南北路上路线有 东西向,两条,南北向两条,左转的四条,右转的四条,如果有图,有如神助啊,很清楚就能看出来了),每条路线上的车辆放到集合中保存(注意,车辆是有序的,我原先使用的一个int 变量来记录只能记录车辆的数目,不符合实际情况)
e:每条路线对应它上面的灯,如果灯得状态是绿灯的话:让集合中车辆减少(集合需要是队列模式的,先入先出,不难想象,先到的车肯定先走),如果状态时绿灯的话,让集合中的车辆增加。

3:思想:
张老师完全是面向对象的思想:
a:路和灯都为独立对象,路的定时器每1s检测灯得状态一次,绿灯减少车辆,红灯增加车辆。(我在路中定义了一个灯对象,然后死循环,每次循环都检测灯的状态,然后在灯里又是一个死循环,让每个灯亮n秒,黑3*n秒(比较混乱,程序可读性不是很高))
b:张老师所有的控制都是在程序中自己控制的(例如,灯黑以后通知哪个灯需要亮,等等,我是完全用程序去控制的,先让他们有一个顺序,然后让它按着这个顺序一直走)


到这里介绍的差不多了,我也相当受教了,通过这一题看下我犯的错误以及原由:
1:自己拼命的去抽象一个模型出来,却完全是用脑子空想,没有想到去画一个图,帮助自己的思维,这是致命的(希望以后再遇到问题能在分析问题上多花一点时间)
2:在思路并不是很清楚的情况下去写程序,本来就是个错误。
3:面向对象的思想并不是很清晰,我写完以后认为自己那个已经完全面向对象了,后来看来很多地方的控制其实是面向过程的。


以后遇到问题希望:
1:先透彻的分析问题,要做到成竹在胸再去写程序,事半功倍
2:思想更加开放一点,更多的去面向对象
3:思维需要更加缜密,考虑到现实情况多一点(例如那个车辆的统计)

多多改进吧,共勉
long95wang 2012-10-22
  • 打赏
  • 举报
回复
题目很好,我回去也看看,嘿嘿!
五柳--先生 2012-10-22
  • 打赏
  • 举报
回复
张孝详老师的这本书看的看不多了,适当的需要做一下小结,因为已经感觉有些东西在淡忘:
1:java编译环境配置
配置环境变量:path 和classpath

2:java中变量的定义方法
int a; char b;
要注意的是数组
int [] a = new int[5];注意点:在java中直接定义int a[5];是错误的,不能直接指定数组的大小。
基本类型之间的强制转换(注,java中所有的对象都继承于一个object类)
要注意类型的大小,转换时会出现的问题。

3:java函数
其中有一点面向对象的东东就是函数的重载:
必须注意,函数重载的意义,对于一个返回值类型相同,函数名相同,参数不同的函数,我们称之为重载

4:java运算符
加减乘除没什么号说的。
这里有一个 && 和 & 的区别和 || 和 |的区别
&& 和 || 不管两个什么对象进行这两个操作,返回的要么是true,要么是false
而&和|运算符则是对某一位进行的操作,两个byte型的进行&(|)操作返回true or false,但是int型或者其他类型的进行&(|)的操作,则是对这个数字的操作,最后得出的结果也是原来的类型的
运算符的优先级我们应该有一个了解,当然不清楚的话我们可以加()嘛,哈哈哈


5:java的流程控制
a:条件判断语句
if(条件)
{
}
else
{
}
条件成立执行if中的语句,不成立执行else的。没什么好说的

b:switch语句
switch()
{
case xx: *** break;
default: *** break;
}

c:while() 和 do...while()
也没什么好说的,不过要清楚他们的区别
while()条件满足,往下执行。
do...while() 不管条件先执行do语句部分,然后判断while条件是否满足,不满足结束,满足执行while中的执行语句

c:for循环
这个用的都可以说是倒背如流了,没啥好说的了吧,不过有看到过一个写法,很新奇的,这里贴一下 for(string [] name :name)呵呵,有兴趣的百度下哦

d:说完循环当然要说一说continue 和break语句啦
他们都是结束循环,continue是结束当下的循环,继续执行下面的循环语句,break是直接跳出整个循环。有点绕口


6:java的核心思想:面向对象
a:封装
将类进行很好的封装,有利于程序的健壮和代码维护
finalize垃圾回收(类似于C++的析构函数,由于垃圾回收器不受程序控制,所以这个基本上算是一个摆设)
System.gc(强制启动垃圾回收器来回收垃圾)

引用数据类型:
引用说白了还是那个对象,将它作为函数形参的话我可以举一个很有意思的例子:
不使用引用的形参传递:这个就相当于有一个孩子叫小明,在学校又克隆了一个小明明,他们并不是同一个人,所以你打了小明明,小明并不知道,也不会痛,对他完全没有作用。所以你对这样的形参的改变,对于那个数据本身没有任何影响
使用引用的形参传递:这个就相当于有一个孩子叫小明,在学校大家给他起了个小名叫小明明,虽然名字不一样,其实他们是同一个人,所以你如果打了小明明其实就是打了小明。所以你如果在函数里对那个形参进行了操作,那么那个数据本身也会跟着改变

static关键字
静态的变量和函数可以直接被调用
不能使用this super关键字
只能调用同一个类里面的静态方法和静态变量


b:继承
记住一个 extends 关键字就哦了,不过要记住,有申明为abstract 的方法,一定要在类中进行实现,否则也只能定义一个abstract的类
这里有一个supper方法:使用这个方法,我们可以在子类的构造函数中调用父类的构造函数
方法的覆盖,子类可以覆盖(重写)父类的方法
final关键字(final的类不能被继承,final的方法不能冲洗,final变量就是常量(应该是类似于C++的const),只能初始化一次)
抽象类:就是abstract类了,如果继承这样的类,你又需要用你的类来进行实例化,那么需要将里面的abstract方法全部 overload一下。
接口:接口也是抽象类,只是我们平时定义的抽象类只是某几个方法是abstract的,但是接口里面的所有方法都是abstract的

c:多态
多态就是一种类型表现出多种状态。
个人认为这里有一个相当重要的概念:
这个是我常常混淆不清的两个东西,我搜索了一把,贴上来
先了解下型构:
int fun(int a,String b) void fun(int a,String b)
这两个型构是相同的,两个函数的参数是一样的,包括类型和顺序

int fun(int a,String b) void fun(String b,int a)
这两个型构是不同的,两个函数的顺序不一样。
重写,英文名是overriding,是指在继承情况下,子类中定义了与其基类中方法具有相同型构的新方法,就叫做子类把基类的方法重写了。这是实现多态必须的步骤。
重载,英文名是overloading,是指在同一个类中定义了一个以上具有相同名称,但是型构不同的方法。在同一个类中,是不允许定义多于一个的具有相同型构的方法的。

7:异常
这个java程序员每天都要说上N扁的词,记得以前坐我边上的java程序员一直说:诶..我这抛了个异常怎么怎么怎么样啊,或者,这里要抛一个异常呀,啥啥的,可见异常在java里面有很重要的地位
异常主要是用来处理问题的工具,活用最终要:
如果我们觉得程序哪里可能会出现问题那就用
try {
//要执行的语句
}
catch (InterruptedException e)
{
//出现异常的处理方法
}

当然如果你发现哪里可能会出现异常,然而你处理不了,或者并不想处理,需要抛一个异常,提醒后面使用你开发的模块的同志要注意异常的处理,方法是很简单,但是一定要记住:throws Exception(具体问题具体对待哦)


finally 关键字:这个是不管异常发生不发生,一定会执行的语句。
最好能养成
try {
//要执行的语句
}
catch (InterruptedException e)
{
//出现异常的处理方法
}
finally
{
}
这样的好习惯哦



package和import没啥好说的了,一个是打包,一个相当于C的include


到多线程部分就不写了

50,526

社区成员

发帖
与我相关
我的任务
社区描述
Java相关技术讨论
javaspring bootspring cloud 技术论坛(原bbs)
社区管理员
  • Java相关社区
  • 小虚竹
  • 谙忆
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧