Part5:Grid Data Structures
Part5:Grid Data Structures
Step1:The AbstractGrid Class
Set 10
The source code for the AbstractGrid class is in Appendix D.
-
Where is the isValid method specified? Which classes provide an implementation of this method?
这个题目在第二阶段的
part2
有提到过一点,isValid()
函数在Grid接口里面声明,在它的接口直接继承的两个继承类里面实现了两个不同的版本:public boolean isValid(Location loc)//BoundedGrid版本 { return 0 <= loc.getRow() && loc.getRow() < getNumRows() && 0 <= loc.getCol() && loc.getCol() < getNumCols(); } public boolean isValid(Location loc)//UnboundedGrid版本 { return true; }
-
Which AbstractGrid methods call the isValid method? Why don’t the other methods need to call it?
函数方法
getValidAdjacentLocations
调用了isValid
方法。这个是最直接的调用,而其他的一些方法没有直接调用isValid
函数,但是它们调用了getValidAdjacentLocations
方法,在valid的判断当中其实也是间接地实现了调用,例如getEmptyAdjacentLocations
方法和getOccupiedAdjacentLocations
方法,还有getNeighbors
方法不是直接调用了isValid
方法,而是调用了getOccupiedAdjacentLocations
方法,之所以调用这些函数的原因是,他们内部的需求在逐级递进,例如说开头判断是否合理,后面一句开头的条件进行判断是否有合理的位置坐标,以及下一个位置的位置坐标等等,这是一个逐渐进益的过程。 -
Which methods of the Grid interface are called in the getNeighbors method? Which classes provide implementations of these methods?
首先我们看看
AbstractGrid
里面的函数情况:for (Location neighborLoc : getOccupiedAdjacentLocations(loc)) neighbors.add(get(neighborLoc)); return neighbors;
这个函数它调用了接口里面的
get()
函数跟getOccupiedAdjacentLocations()
函数,后者是在本文件之下完成定义的,而前者是在该类的两个继承类BoundedGrid
和UnboundedGrid
当中实现的。 -
Why must the get method, which returns an object of type E, be used in the getEmptyAdjacentLocations method when this method returns locations, not objects of type E?
get()
函数的作用是:返回在网格中的指定对象(Object)的引用,如果说该引用为空的话,也就是所在网格当中其实该对象并不存在,所以说我们的get函数是用来判断指定的位置是否存在对象的依据,getEmptyAdjacentLocations
方法它调用了当前实现的get()函数来检测目标位置是否存在一个单位,如果存在的话跳过去,不存在的话就把当前位置的坐标位置加入到可执行的队列当中(代表该位置是空的)。这个get()函数是检测当前位置是否为空的唯一可用标准。 -
What would be the effect of replacing the constant Location.HALF_RIGHT with Location.RIGHT in the two places where it occurs in the getValidAdjacentLocations method?
先看看代码:
public ArrayList<Location> getValidAdjacentLocations(Location loc) { ArrayList<Location> locs = new ArrayList<Location>(); int d = Location.NORTH; for (int i = 0; i < Location.FULL_CIRCLE / Location.HALF_RIGHT; i++) { Location neighborLoc = loc.getAdjacentLocation(d); if (isValid(neighborLoc)) locs.add(neighborLoc); d = d + Location.HALF_RIGHT; } return locs; }
这个函数的功能是,找到Loc位置周围可能的八个位置的合理网格单元,这的话八个位置就是字\(NORTH\)开始每\(HALF\_RIGHT\)一次出现的位置,总共是8个,那么如果把\(Location.HALF\_RIGHT\)换成\(Location.RIGHT\),那么整个坐标转一周也就是4次,也就是方向只剩下东南西北,那么能够找到的可能位置就只有这4个方位了,而不是8个。
Step2:The BoundedGrid Class
Set 11
The source code for the BoundedGrid class is in Appendix D.
-
What ensures that a grid has at least one valid location?
public BoundedGrid(int rows, int cols) { if (rows <= 0) throw new IllegalArgumentException("rows <= 0"); if (cols <= 0) throw new IllegalArgumentException("cols <= 0"); occupantArray = new Object[rows][cols]; }
如果当前的地图不合理,构造函数会给出一个警告,以保证我们在执行的时候最少是有一个位置是合理的。
-
How is the number of columns in the grid determined by the getNumCols method? What assumption about the grid makes this possible?
// Note: according to the constructor precondition, numRows() > 0, so // theGrid[0] is non-null. return occupantArray[0].length;
该方法返回
occupantArray
第一行的列数,以保证我们接下来的判断当中网格至少是一个单位的网格(也就是至少一行一列)。 -
What are the requirements for a Location to be valid in a BoundedGrid?
地图的笛卡尔坐标的表示是从\((0,0)\)开始的,所以我们在构建有边图的时候,需要确保我们的给出位置的信息要满足:\(max\_row\geq{rows}\geq 0\)而且\(max\_col\geq{cols}\geq 0\).
In the next four questions, let r = number of rows, c = number of columns, and n = number of occupied locations.
-
What type is returned by the getOccupiedLocations method? What is the time complexity (Big-Oh) for this method?
函数返回一个
ArrayList<Location>
列表,时间复杂度满足\(O(r\ast{c})\),因为要找到被占据的位置我们需要了解每个位置的情况,所以说在一个二维的数组里面我们需要执行一个遍历的操作,所以会时间复杂度就是遍历操作的复杂度。 -
What type is returned by the get method? What parameter is needed? What is the time complexity (Big-Oh) for this method?
前面提到过了,一个
get()
函数返回的内容是一个\(E\)类型的对象,用来检测该位置是否存在一个对象,它的参数是一个Location类型的变量,该函数是线性操作,没有涉及遍历的操作,在合理的笛卡尔坐标上面(一个)操作,所以时间复杂度\(O(1)\)。 -
What conditions may cause an exception to be thrown by the put method? What is the time complexity (Big-Oh) for this method?
public E put(Location loc, E obj) { if (!isValid(loc)) throw new IllegalArgumentException("Location " + loc + " is not valid"); if (obj == null) throw new NullPointerException("obj == null"); // Add the object to the grid. E oldOccupant = get(loc); occupantArray[loc.getRow()][loc.getCol()] = obj; return oldOccupant; }
当前的位置信息不是
isValid()
的时候,抛出一个异常,当前的Object为空的时候抛出一个异常,执行的步骤是线性的一个点,所以时间复杂度满足\(O(1)\)。 -
What type is returned by the remove method? What happens when an attempt is made to remove an item from an empty location? What is the time complexity (Big-Oh) for this method?
当前方法返回的是一个\(E\)类型的Object对象,如果尝试删除一个空的位置,那么当前方法会抛出一个异常,同理,时间复杂度还是\(O(1)\)。
-
Based on the answers to questions 4, 5, 6, and 7, would you consider this an efficient implementation? Justify your answer.
根据时间复杂度来看的话,我们的实现基本上处于一个高效的状态,基本上所有的实现都满足一个\(O(1)\)的需求,唯一不一样的是
getOccupiedLocations
方法,它的时间复杂度会稍微有点大。
Step3:The UnboundedGrid Class
Set 12
The source code for the UnboundedGrid class is in Appendix D.
-
Which method must the Location class implement so that an instance of HashMap can be used for the map? What would be required of the Location class if a TreeMap were used instead? Does Location satisfy these requirements?
Location类必须实现
hashCode
和equals
方法,当equals
方法判断两个位置一样时候,我们的哈希函数必须具备同样的键值,并且尽量做到减少哈希冲突,Location类实现比较的接口。因此,Location必须实现compareTo
方法。当调用equals方法时测试的两个位置相等的时候,compareTo
方法返回0。Location类满足所有要求。 -
Why are the checks for null included in the get, put, and remove methods? Why are no such checks included in the corresponding methods for the BoundedGrid?
unboundgrid
使用HashMap作为其数据结构来保存网格中的项。全部非空位置在无界网格中有效。unboundGrid的isValid
方法始终返回true,因为它没有边界。在Map对象中,key的合法值是空,在
unboundgrid
中,null不是有效位置。因此,get
、put
和remove
必须检查location参数,当参数为空时抛出NullPointerException。在使用有界网格中的Location的时候,我们应当使用
isValid()
,如果isValid方法中的location参数为null,则尝试访问getRow()
将导致抛出NullPointerException。如果编写的代码在调用get、put和remove之前未调用isValid方法方法,尝试访问位置getRow()在这些方法中也会导致抛出NullPointerException。public E put(Location loc, E obj) { if (loc == null) throw new NullPointerException("loc == null"); if (obj == null) throw new NullPointerException("obj == null"); return occupantMap.put(loc, obj); }
-
What is the average time complexity (Big-Oh) for the three methods: get, put, and remove? What would it be if a TreeMap were used instead of a HashMap?
get``、
put和
remove```的平均时间复杂度为\(O(1)\)。如果使用TreeMap而不是HashMap,则平均时间复杂度为\(O(log_2n)\),其中n是网格中已占用的位置数。
-
How would the behavior of this class differ, aside from time complexity, if a TreeMap were used instead of a HashMap?
大多数情况下,
getOccupiedLocations
方法将以不同的顺序返回occupants
。HashMap中的键(位置)基于使用哈希表的大小。当键集被访问时,访问该键的顺序
取决于它在哈希表中的位置。
HashMap
将它的key存储在一个平衡的二叉搜索树中,并使用中序遍历
遍历遍历该树。键集中的键将按升序被访问。
-
Could a map implementation be used for a bounded grid? What advantage, if any, would the two-dimensional array implementation that is used by the BoundedGrid class have over a map implementation?
map
可以被用来实现BoundedGrid
,如果使用HashMap的话,那么实现该类的方法getOccupiedLocations
的时间复杂度将会到达\(O(n)\),在向通的情况之下map
将会使用更多的内存,因为map需要存储的是Object包括它的Location,而我们的二维向量就不一样了,他只需要存储Object,位置的话,二维向量的下标会给出索引,不需要特意存储。public ArrayList<Location> getOccupiedLocations() { ArrayList<Location> a = new ArrayList<Location>(); for (Location loc : occupantMap.keySet()) a.add(loc); return a; }