看了张老师的交通灯面试题的讲解,收获颇丰。老张的确是牛人。在刚拿到题的时候,我自己想了想,发现没有一个清晰的思路。可是等他讲的时候,我发现很多地方比我想的确实要好。而且思路非常清晰。
那,看完了讲解,能不能按照自己的思路作呢?
我尝试了下,代码如下:
思路(和张老师有些不同):
1。对象的抽取: 舞台(整个的场景,用来做main的),车,路,交通灯,交通控制系统。这部分基本一样。
但是,我不是把灯分成12个,而是把路分成12条。
我是这么想的,在实际中,灯有红绿,而一套交通控制系统,只要有两套红绿灯就够了。
而路和交通灯的关系,是通过在场景里设定的,而不是放到路里面,或者灯的本身里面。
这么做,好处是,如果增删路的条数,不会动很大的东西。
关于灯,张老师在做的时候,考虑的是红,绿两种状态,可是,需求里说,“同方向等待车辆,应先放行直行车辆,而后放行左转车辆”,并且考虑到日常生活,如果只有一条车道,直行车和左转的车,如果交叉进入一个车道,那么就不可能达到这个要求,所以,只能是按照2车道设计,即:仅左行车道,和直行车道。
于是,将绿灯分成了左行和直行两种。
2. 对象关系分析: a) 车,路,灯 都在舞台上 b) 车在马上 c) 灯受控制系统的控制 d) 灯有两个颜色:红,绿 e) 十字路口,所以有2条路,受2组灯控制 f) 红灯后是绿灯 g) 绿灯先放真行,而后放左拐 h) 右行的车辆不受灯的控制 i) 绿灯的总时间和红灯的总时间是一致的,否则,会出现同时为绿灯,或者同时为红灯的情况。在场景中,共有如下单独的线程:
1。随机增加车辆的线程
2。12条不同方向的路的线程,用来扫描,对比条件,然后去掉排队的车辆。
3。独立的交通控制系统。我灯的变化,和路,车毫无关系。但是,路上的车的情况,要根据我的灯的情况而变化。
3. 情景设计: a) 右行车不受灯的控制,所以任何时候,只要有车都可以直接通过。 b) 设起始点是E2W方向为红灯 i. W2E,E2W方向,红灯 ii. 前20秒 1. N2S方向,直行绿灯 2. S2N方向,直行绿灯 iii. 后20秒 1. N2S方向,左转绿灯 2. S2N方向,左转绿灯 iv. 30秒后,S2N,N2S方向变为红灯 c) 进入第二个场景,S2N,N2S方向变为红灯 i. S2N,N2S方向变为红灯 ii. 前20秒: 1. W2E方向,直行绿灯 2. E2W方向,直行绿灯 iii. 后10秒 1. W2E方向,左转绿灯 2. E2W方向,左转绿灯 iv. 30秒后,回到初始场景(但是每条路的存留车辆不变)所有的场景,都放到舞台里导演。
关于这一部分,我一直没有把握,感觉有些东西还能抽出来。
其实,我的想法很简单。大概的步骤可以概括如下:
1。有一个舞台:main
2。舞台加入道路
3。舞台加入控制系统
4。在路上加车
5。道路上车的数量,根据控制系统改变
但是这么搞,大量的逻辑都放到了5的里面搞的代码很难看。。。等培训完了,如果有好的思路,再修改吧。搞了一下午,头大了,不想搞了。。。。
下面就是所有的代码:
Vehicle.java
package com.linuxgg.interview.trafic; public class Vehicle { private Direction whereTogo; private String name = null; // 生成去来自哪里去往何方的车 public Vehicle(Direction whereTogo, String name) { this.whereTogo = whereTogo; this.name = name; } // 根据需求,车辆过路口要花1秒时间 public void move() { System.out.println("车的信息:" + name + " Direction [" + whereTogo.name() + "], Bye !!"); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public Direction getWhereTogo() { return whereTogo; } public void setWhereTogo(Direction whereTogo) { this.whereTogo = whereTogo; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
Lamp.java
package com.linuxgg.interview.trafic; public enum Lamp { Red(30), GreenAhead(20), GreenLeft(10); private Lamp(int i) { this.time = i; } public int getTime() { return time; } public void setTime(int time) { this.time = time; } private int time = 0; }
Road.java
package com.linuxgg.interview.trafic; import java.util.*; public class Road { // 路的方向 private Direction direction = null; // 路上车的队列 public List<Vehicle> roadQueue = new ArrayList<Vehicle>(); // 构造函数用来生成去往理的路线 public Road(Direction direction) { System.out.println("生成去往" + direction.name() + "方向的路线。"); this.direction = direction; } // 在这个路线上再增加一两车 public Boolean add(Vehicle vehicle) { System.out.println("开往方向[" + direction + "]的路上,增加了一辆车:[" +vehicle.getWhereTogo() +":"+vehicle.getName() + "]"); return roadQueue.add(vehicle); } // 从路的队列里删掉一辆车 public void remove(int i) { //System.out.println("Start to remove."); if (roadQueue.size() > 0) { // 先开走小车,没啥实际意义,好玩儿而已,按照需求,开走需要1秒 roadQueue.get(i).move(); System.out.println("移出的车辆:[" + this.getDirection().name() + "]: " + roadQueue.remove(0).getWhereTogo()); } else { //System.out.println("[" + this.getDirection() + "]: 当前没有车"); } } public List<Vehicle> getRoadQueue() { return roadQueue; } public void setRoadQueue(List<Vehicle> roadQueue) { this.roadQueue = roadQueue; } public Direction getDirection() { return direction; } public void setDirection(Direction direction) { this.direction = direction; } }
Stage.java -->main
package com.linuxgg.interview.trafic; import java.util.*; public class Stage { // 步骤: // 搞好舞台,演员开始上场 // 设定路 // 添加交控制系统 // 添加车 /** * @param args */ // 控制系统只有一套,所以直接给了静态 static TraficControlSystem tf; // 道路只有一套,所以直接给了静态 static List<Road> roadList = new ArrayList<Road>(); public static void main(String[] args) { System.out.println("舞台搞好,演员开始上场"); // 加载道路 for (int i = 0; i < Direction.values().length; i++) { roadList.add(new Road(Direction.values()[i])); } // 加载交通控制系统 done tf = new TraficControlSystem(); new Thread(new Runnable() { @Override public void run() { tf.start(false, 10); }; }).start(); // 把车放到路上,随机。done new Thread(new Runnable() { @Override public void run() { addVehicles2Road(roadList); } }).start(); // 根据roadList中的路,创建所有路的线程。 // 将每一条路当作一个线程,然后2秒扫描一次,如果发现有车,且符合条件,就删除 // 下面是路的分类 // NS方向 直行 // N2S,S2N // NS方向,左行 // N2E,S2W // WE方向 直行 // W2E,E2W // WE方向,左行 // W2N,E2S // 右行 // S2E,E2N,N2W,W2S for (final Road r : roadList) { new Thread(new Runnable() { public void run() { while (true) { try { // 每条路,每格200毫秒扫描一次 Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } // System.out.println(r.toString()); // System.out.println("Start checking..." // + r.getDirection()); // 还有四种情况是右转,只要队列里有车就减少,和灯无关 if (r.getDirection().name() == "S2E" || r.getDirection().name() == "E2N" || r.getDirection().name() == "N2W" || r.getDirection().name() == "W2S") { r.remove(0); // 如果是NS方向为红灯 } else if (tf.lampForNS.equals(Lamp.Red)) { // WE方向直行 if ((r.getDirection().name() == "W2E" || r .getDirection().name() == "E2W") && tf.lampForWE.equals(Lamp.GreenAhead)) { r.remove(0); // WE方向左行 } else if ((r.getDirection().name() == "W2N" || r .getDirection().name() == "E2S") && tf.lampForWE.equals(Lamp.GreenLeft)) { r.remove(0); } else { // System.out.println("这条路上的车没被执行:" // + r.getDirection().name()); } // 如果NS方向不为红灯,也就是绿灯 } else { // NS方向直行 if ((r.getDirection().name() == "N2S" || r .getDirection().name() == "S2N") && tf.lampForNS.equals(Lamp.GreenAhead)) { r.remove(0); // NS方向左行 } else if ((r.getDirection().name() == "N2E" || r .getDirection().name() == "S2W") && tf.lampForNS.equals(Lamp.GreenLeft)) { r.remove(0); } else { // System.out.println("这条路上的车没被执行:" // + r.getDirection().name()); } } } } }).start(); } } // 加载车到对应的路面上 done private static void addVehicles2Road(List<Road> roadList) { while (true) { try { Thread.sleep(100); // 获取随机数量-〉路的数量 int roadListRandom = new Random().nextInt(roadList.size()); // 根据路的方向,添加相应的车 roadList.get(roadListRandom).add( new Vehicle( roadList.get(roadListRandom).getDirection(), "开往[" + roadList.get(roadListRandom) .getDirection() + "]的车[" + System.currentTimeMillis() + "]")); // System.out.println(roadList.get(i).getRoadQueue().size()); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } showCountFromRoad(roadList); } } // 显示每条路的状态 private static void showCountFromRoad(List<Road> roadList) { // 添加之后,显示当前的每条路上的车的情况 Iterator<Road> it = roadList.iterator(); while (it.hasNext()) { Road r = (Road) it.next(); // System.out.println(r.getDirection()); System.out.print("[" + r.getDirection() + ":" + r.getRoadQueue().size() + "] "); } System.out.println(); } }
TraficControlSystem.java
/*灯的分组: * 两块灯组: * WE, SN 分别控制东西方向和南北方向。 * * 关系: * 1.WE为红,则SN必然为绿 * 2.30秒替换一次红绿 * 3.绿色的时候,先20秒直行,然后10秒左转 * */ package com.linuxgg.interview.trafic; public class TraficControlSystem { // 默认两套灯,都是红的 Lamp lampForNS = Lamp.Red; Lamp lampForWE = Lamp.Red; // 希望这套控制系统运行多少次,默认是永久执行 public void start(boolean endless, int loopTime) { if (endless) { while (true) { changeLamp(); } } else { for (int i = 0; i < loopTime; i++) { changeLamp(); } } } // 灯的色彩开始变化 private void changeLamp() { int lampTime = 0; // 一共有多少个灯,顺次闪亮 for (int i = 0; i < Lamp.values().length; i++) { lampForNS = Lamp.values()[i]; System.out.println("NS方向,当前的灯是[" + lampForNS.name() + "]."); // 根据当前的灯的颜色判断需要变化多少秒 try { lampTime = lampForNS.getTime(); while (lampTime > 0) { // 因为红灯与绿灯总合的时间相同,所以,去掉left的时间,就是直行的时间,又因为直行在前,所以,是和left的设定时间做比较. if (lampForNS.equals(Lamp.Red)) { lampForWE = Lamp.GreenAhead; if (lampTime <= Lamp.GreenLeft.getTime()) { // 在NS方向的红灯时间内,且去掉了直行绿灯的时间,剩下的,就是左转绿灯的时间。 lampForWE = Lamp.GreenLeft; } } System.out.println("[NS]方向的[" + lampForNS.name() + "]灯还剩[" + (lampTime) + "]秒. 此时WE方向的灯为[" + lampForWE.name() + "]"); lampTime--; Thread.sleep(1000); } // 当NS方向的灯不是红色的时候,WE方向的灯必然为红色 if (lampForNS.equals(Lamp.Red)) { lampForWE = Lamp.Red; } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } /* public static void main(String[] args) { final TraficControlSystem tf = new TraficControlSystem(); new Thread(new Runnable() { @Override public void run() { tf.start(false, 2); } }).start(); new Thread(new Runnable() { @Override public void run() { while (true) { try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("-----NS:" + tf.lampForNS.name() + " WE:" + tf.lampForWE.name()); } } }).start(); } */ }
Vehicle.java, 本来为了更有意思,想完善它,不过现在真的搞不动了。。。还得吃饭啊。。。
package com.linuxgg.interview.trafic; public class Vehicle { private Direction whereTogo; private String name = null; // 生成去来自哪里去往何方的车 public Vehicle(Direction whereTogo, String name) { this.whereTogo = whereTogo; this.name = name; } // 根据需求,车辆过路口要花1秒时间 public void move() { System.out.println("车的信息:" + name + " Direction [" + whereTogo.name() + "], Bye !!"); try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public Direction getWhereTogo() { return whereTogo; } public void setWhereTogo(Direction whereTogo) { this.whereTogo = whereTogo; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
代码跑了一段时间之后的结果:
可以清楚的发现,
在12条道路增加车辆的几率基本一致的情形下:
1。右行车道,交通毫无压力,基本为空
2。4条左行车道数量要高于4条直行道,因为直行的绿灯时间要长于左行。
顺便感叹一下双井桥的红绿灯,擦,兄弟,你要是不会,赶紧来黑马学吧!!! 一套交通系统搞毛阿。。。你的算法绝对有问题!
今天又回头看了一下,发现里面的有些注释不太对,呵呵,那个是写的时候搞的,后来又调试着玩儿,所以作了些改动,注释没更新,都很简单,可以忽略。这个题,比银行那个排号的确实繁琐。。。