笔经面经分享之亚马逊笔试题第二弹。希望通过本帖,能够对正在找工作的同学或者将要找工作的同学提供微薄之力。
--小站小编
1 笔试题下面是来自于亚马逊的编程笔试题,感兴趣的同学可以先自己想想解题思路:
Please implement a simple logic rule, the input is a set of logic formula, the format is like a1,a2,a3,…,an->b, which means a1,a2,a3,…an can deduce b. It meets the following rules:
1. If A->B B->C then A->C
2. If A,B->C D->A then D,B->C
3. If A,B->C then B,A->C
The input contains two lines:
1. The first line contains n (100>n>0) base formulas, separated by spaces
2. The second line contains m (100>m>0) formulas that need to check true or false according to above n formulas, separated by spaces
The output should be m lines, if the i-th formula in the second line can be deduced by the n base formulas, the i-th line should be "true", otherwise, "false".
Each element is a case sensitive alphabet, to simplify the parsing work, “->” is replaced by ">", there might be more than 1 character on the left side and only one on the right side, the following is an example: A,B,C>D
Sample Input and Output
Input
A>B B>C
A>C
Output
true
Input
A,B>C D,E>A E>B
E,D>C F,E>C
Output
true
false
Input
A>B A,B>C
A>C
Output
true
Please complement the matchRule method in Solution class:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Solution {
public static void main(String[] args) throws IOException {
InputStreamReader isr = new InputStreamReader(System.in);
BufferedReader br = new BufferedReader(isr);
String baseRules = br.readLine();
String checkingRules = br.readLine();
matchRule(baseRules, checkingRules);
br.close();
isr.close();
}
private static void matchRule(String baseRules, String checkingRules) {
}
}
--------------我是华丽丽的分隔线----------------
首先让我们对比一下几种数据结构:线性表,树,图。
在线性表中,数据元素之间是被串起来的,仅有线性关系,每个元素只有一个直接前驱和一个直接后继。比如说排队买票。
在树形结构中,数据元素之间有着明显的层次关系,并且每一层上的数据元素可能和下一层中多个元素相关,但只能和上一层中一个元素相关。比如说一对父母可以有多个孩子,但是每个孩子只能有一对父母。
图是一种较线性表和树更加复杂的数据结构。在图形结构中,结点之间的关系可以是任意的,图中任意两个数据元素之间都可能相关。比如说人与人之间的关系是一种复杂的多对多的关系,所以研究人际关系很自然地就会使用图这种数据结构。
图(Graph)是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:G(V, E),其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合。
如果图中任意两个顶点之间的边是有方向的,则称该图为有向图,否则称该图为无向图。
3 拓扑排序简介我们会把施工过程,生产流程,软件开发,教学安排等都当成一个项目工程来对待,所有的工程都可分为若干个“活动”的子工程。如下图所示:
这是使用大脑作为生物传感器来检测低血糖的流程图。从图的角度来看,它是一个有向图。在一个表示工程的有向图中,用顶点表示活动,用边表示活动之间的优先关系,这样的有向图是一个使用顶点表示活动的网,称为AOV网(Activity On Vertex Network)。AOV网中的边表示活动之间存在的某种制约关系。
设G(V, E)是一个具有n个顶点的有向图,V中的顶点序列v1, v2, v3....vn, 满足若从顶点vi到vj有一条路径,则我们称这样的顶点序列为一个拓扑序列。
上图中这样的AOV网的拓扑序列不止一条。序列1,2,3,4,5,6,7,8是一个拓扑序列,1,2,5,4,6,3,7,8也是一条拓扑序列。
4 拓扑排序算法简介对AOV网进行拓扑排序的基本思路是:从AOV网中选择一个入度为0的顶点输出,然后删去此顶点,并删除该顶点指向其他顶点的边,继续重复此步骤,直到输出全部顶点或者AOV网中不存在入度为0的顶点为止。(注:入度是指一个活动的直接先决活动的数量,以上图中的活动7为例,它的入度为2,分别是活动3和活动6)
5 回到笔试题基础知识介绍完毕,如果大家还有不明白的地方,可以先找找相关的资料参考一下。
5.1 对题目的理解
要解决一个问题,首先需要明确问题:
输入n条(100>n>0)条正确的逻辑推导语句,m条(100>m>0)待检验的的逻辑推导语句。所以问题是如何根据正确的推导语句判断待检验的逻辑推导语句的正确性。
逻辑推导语句的一个例子:A,B,C>D。结合题目的内容,逻辑推导语句有如下特点:
1. 语句中的每个元素都是大小写敏感的字母
2. 左边是1个或多个字母,以逗号分隔
3. 右边有且仅有一个字母
4. 中间的推导符号为“>”
5.2 为什么可以使用拓扑排序可以解决问题
从题目中的几个例子可以看出,所谓的逻辑推导关系,其实也可以理解成AOV网中各个活动之间优先关系。正是因为有这个相似性,所以我们可以利用拓扑排序来解决这个问题。
5.3 使用拓扑排序解决问题
利用拓扑排序解决这个问题,我们的步骤应该是:
1. 逐条解析n条正确的逻辑推导语句,并生成一个AOV网拓扑结构
2. 逐条解析m条待检验的逻辑推导语句,使用拓扑排序算法逐条检验。
3. 输出检验结果
使用拓扑排序算法逐条检验一条逻辑推导语句的方法:
遍历逻辑推导语句左边的结点,对于每个结点,查看该结点在步骤1生成的AOV网中的入度是否为0,如果为0,则把该结点关联的所有结点的入度减1(相当于在AOV网中,把该结点指向其他结点的边删除掉,也就相当于将它从AOV网中删除掉),最后当遍历完毕时,查看逻辑推导语句右边的结点的入度是否为0,如果为0,则说明推导成功,该语句是正确的;否则该语句是错误的。
最后的实现代码(java)如下:
- private static void matchRule(String baseRules, String checkingRules) {
- if (!validateInput(baseRules) || !validateInput(checkingRules)){
- return;
- }
- RuleTopology topology = new RuleTopology(baseRules);
- List<String> result = topology.checkRules(checkingRules);
- printResult(result);
- }
- static class RuleTopology {
- static String RULE_SEPERATOR = " ";
- static String RULE_CONDITION_SEPERATOR = ",";
- static String RULE_DEDUCTION_SIGNAL = ">";
- static String RULE_RESULT_TRUE = "true";
- static String RULE_RESULT_FALSE = "false";
- Map<VertexNode, Set<VertexNode>> topology = null;
- public RuleTopology(String baseRules) {
- buildTopology(baseRules.split(RULE_SEPERATOR));
- }
- public List<String> checkRules(String checkingRules) {
- List<String> result = new ArrayList<String>();
- for (String rule : checkingRules.split(RULE_SEPERATOR)) {
- result.add(checkRule(deepCopyTopology(), rule));
- }
- return result;
- }
- public VertexNode getNode(Map<VertexNode, Set<VertexNode>> topology, String name) {
- VertexNode colusionNode = null;
- Set<VertexNode> topologyNodes = topology.keySet();
- for (VertexNode node : topologyNodes) {
- if (node.getName().equals(name)) {
- colusionNode = node;
- break;
- }
- }
- return colusionNode;
- }
- String checkRule(Map<VertexNode, Set<VertexNode>> topology, String rule) {
- //There's two parts of a rule, e.g. A,B>C:
- //condition part: A,B
- //conclusion part: C
- String[] parts = rule.split(RULE_DEDUCTION_SIGNAL);
- String[] conditionPart = parts[0].split(RULE_CONDITION_SEPERATOR);
- String conclusionPart = parts[1];
- //If the input number of VertexNode with the same name as
- //conclusionPart is zero from the beginning, then return false,
- //because in this situation the rule can not be checked based
- //on the base rules.
- VertexNode colusionNode = getNode(topology, conclusionPart);
- if (colusionNode.getInNum() == 0) {
- return RULE_RESULT_FALSE;
- }
- //For each VertexNode with the same name in the conditionPart,
- //decrease the input number of each VertexNode it connected.
- for (String nodeName : conditionPart) {
- VertexNode findedNode = getNode(topology, nodeName);
- if (findedNode != null) {
- decreaseConVexInNum(topology, findedNode);
- }
- }
- //Finally, all the input number of each VertexNode in topology
- //have been decreased, then to see whether the input number
- //of the VertexNode with the same name as conclusionPart is
- //zero.
- return colusionNode.getInNum()==0?RULE_RESULT_TRUE:RULE_RESULT_FALSE;
- }
- /**
- * A VertexNode may have some connected VertexNodes, decrease the
- * input number of a VertexNode may trigger the input number of
- * its connected VertexNodes to be zero
- * @param topology
- * @param node
- */
- void decreaseConVexInNum(Map<VertexNode, Set<VertexNode>> topology, VertexNode node) {
- if (node.getInNum() == 0) {
- Set<VertexNode> edgeNodes = topology.get(node);
- for (VertexNode edgeNode : edgeNodes) {
- edgeNode.decreaseInNum();
- decreaseConVexInNum(topology, edgeNode);
- }
- }
- }
- /**
- * Used to deep copy topology. To check the rules in checkingRules,
- * it needs to change the topology. So before checking each rule,
- * we should deep copy the topology to keep the original topology
- * unchanged.
- * @return
- */
- @SuppressWarnings("unchecked")
- Map<VertexNode, Set<VertexNode>> deepCopyTopology() {
- Map<VertexNode, Set<VertexNode>> result = null;
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- ObjectOutputStream oos = null;
- ObjectInputStream ois = null;
- try {
- oos = new ObjectOutputStream(bos);
- oos.writeObject(topology);
- ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
- result = (Map<VertexNode, Set<VertexNode>>) ois.readObject();
- } catch (IOException e) {
- e.printStackTrace();
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- }
- return result;
- }
- void buildTopology(String[] rules) {
- topology = new HashMap<VertexNode, Set<VertexNode>>();
- for (String rule : rules) {
- parseRule(rule);
- }
- }
- void parseRule(String rule) {
- //There's two parts of a rule, e.g. A,B>C:
- //condition part: A,B
- //conclusion part: C
- String[] parts = rule.split(RULE_DEDUCTION_SIGNAL);
- String[] conditionPart = parts[0].split(RULE_CONDITION_SEPERATOR);
- String conclusionPart = parts[1];
- //List ruleNodes contains all the names of nodes in the rule
- //(includes condition part and conclusion part)
- List<String> ruleNodeNames = new ArrayList<String>(Arrays.asList(conditionPart));
- ruleNodeNames.add(conclusionPart);
- //If nodes in rule have not been exit in topology, add to
- //topology
- for (String nodeName : ruleNodeNames) {
- if ( !topology.containsKey(new VertexNode(nodeName)) ) {
- topology.put(new VertexNode(nodeName), new HashSet<VertexNode>());
- }
- }
- //Find the VertexNode in topology with the same name with
- //conclusionPart
- VertexNode colusionNode = getNode(topology, conclusionPart);
- //For each node in condition part, add the related VertexNode
- //For node in conclusion part, increase the input number by
- //one
- for (String nodeName : conditionPart) {
- topology.get(new VertexNode(nodeName)).add(colusionNode);
- colusionNode.increaseInNum();
- }
- }
- static class VertexNode implements Serializable {
- private static final long serialVersionUID = 2218090065058270731L;
- private int inNum = 0;
- private String name = "";
- public VertexNode(String name) {
- this.name = name;
- }
- public void increaseInNum() {
- ++inNum;
- }
- public void decreaseInNum() {
- --inNum;
- }
- public int getInNum() {
- return inNum;
- }
- public String getName() {
- return name;
- }
- @Override
- public int hashCode() {
- final int prime = 31;
- int result = 1;
- result = prime * result + ((name == null) ? 0 : name.hashCode());
- return result;
- }
- @Override
- public boolean equals(Object obj) {
- if (this == obj)
- return true;
- if (obj == null)
- return false;
- if (getClass() != obj.getClass())
- return false;
- VertexNode other = (VertexNode) obj;
- if (name == null) {
- if (other.name != null)
- return false;
- } else if (!name.equals(other.name))
- return false;
- return true;
- }
- }
- }
- static void printResult(List<String> result) {
- for (String item : result) {
- System.out.println(item);
- }
- }
- static boolean validateInput(String input) {
- return true;
- }
由于水平有限,错误在所难免,欢迎批评指正。谢谢!
——来自##17##朱元友