Step1:The AbstractGrid Class
Set 10
- isValid方法在哪里指定?哪些类提供此方法的实现?
- 回答:isValid()方法在Grid接口中被定义;在UnBoundedGrid类与BoundedGrid类中被实现。
- 代码:
// @file:GridWorldCode\framework\info\gridworld\grid\BoundedGrid.java
// @line: 60-64(BoundedGrid) 54-56(UnboundedGrid)
//(BoundedGrid)
public boolean isValid(Location loc)
{
return 0 <= loc.getRow() && loc.getRow() < getNumRows()
&& 0 <= loc.getCol() && loc.getCol() < getNumCols();
}
//(UnboundedGrid)
public boolean isValid(Location loc)
{
return true;
}
- 哪些AbstractGrid类中的方法调用了isValid方法?为什么其他方法不需要调用它?
- 回答:
a. getValidAdjacentLocations()函数直接调用了isVaild()函数;
b. getEmptyAdjacentLocations()和getOccupiedAdjacentLocations()调用了函数getValidAdjacentLocations()间接调用isValid()函数。 只有这三个函数需要通过isVaild()函数的值来判断是否需要添加位置。 - 代码:
- 回答:
// @file:GridWorldCode\framework\info\gridworld\grid\AbstractGrid.java
// @line: 36-49 54 65
//getValidAdjacentLocations()函数
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;
}
//getEmptyAdjacentLocations()函数中的调用
for (Location neighborLoc : getValidAdjacentLocations(loc))
//getOccupiedAdjacentLocations()函数中的调用
for (Location neighborLoc : getValidAdjacentLocations(loc))
- 在getNeighbors方法中调用了Grid接口的哪些方法?哪些类提供这些方法的实现?
- 回答:
a. 调用了接口Grid()中的get()函数以及getOccupiedAdjacentLocations()函数。
b. get()函数在BoundedGrid类与UnboundedGrid类中被实现;getOccupiedAdjacentLocations()函数在Abstrct()类中被实现。 - 代码:
- 回答:
// @file: GridWorldCode\framework\info\gridworld\grid\AbstractGrid.java
// @file: GridWorldCode\framework\info\gridworld\grid\BoundedGrid.java
// @file: GridWorldCode\framework\info\gridworld\grid\UnboundedGrid.java
// @line: 62-71 85-91 66-71(对应文件的行数)
//getOccupiedAdjacentLocation()函数
public ArrayList<Location> getOccupiedAdjacentLocations(Location loc)
{
ArrayList<Location> locs = new ArrayList<Location>();
for (Location neighborLoc : getValidAdjacentLocations(loc))
{
if (get(neighborLoc) != null)
locs.add(neighborLoc);
}
return locs;
}
//BoundedGrid类中get()函数
public E get(Location loc)
{
if (!isValid(loc))
throw new IllegalArgumentException("Location " + loc
+ " is not valid");
return (E) occupantArray[loc.getRow()][loc.getCol()]; // unavoidable warning
}
//UnboundedGrid类中get()函数
public E get(Location loc)
{
if (loc == null)
throw new NullPointerException("loc == null");
return occupantMap.get(loc);
}
- 当get方法返回loaction而不是E类型的对象时,为什么必须在getEmptyAdjacentLocations方法中使用返回E类型的对象的get方法?
- 回答:因为getEmptyAdjacentLocations()函数的功能是通过使用get()函数找到一个临近的空白位置;而get()函数则是可以返回给定某个位置上是否有物体的存在,如果不存在返回null,只能一次对一个物体进行判断。
- 代码:
//getEmptyAdjacentLocations()函数
//@file:GridWorldCode\framework\info\gridworld\grid\AbstractGrid.java
//@line: 51 - 60
public ArrayList<Location> getEmptyAdjacentLocations(Location loc)
{
ArrayList<Location> locs = new ArrayList<Location>();
for (Location neighborLoc : getValidAdjacentLocations(loc))
{
if (get(neighborLoc) == null)
locs.add(neighborLoc);
}
return locs;
}
- 在getValidAdjacentLocations方法中出现的两个位置中,用Location.RIGHT替换常量Location.HALF_RIGHT会产生什么影响?
-
回答:如果Location.HALF_RIGHT被替换为Location.RIGHT,那么一个位置相邻的有效位置将从8个减少到四个,东北、东南、西北、西南这四个位置将无法被选中。
我认为此条回答不需要附上代码以作补充解释。
-
Step2:The BoundedGrid Class
Set 11
- 是什么确保grid具有至少一个有效位置?
- 回答:BoundedGrid类的构造函数当判断到row <= 0 或者 col <= 0时候会抛出一个错误。这样就确保了grid中至少有一个有效位置。
- 代码:
//BoundedGrid的构造函数
// @file: GridWorldCode\framework\info\gridworld\grid\BoundedGrid.java
// @line: 39-46
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];
}
- 如何通过getNumCols方法确定grid中的列数?关于grid的什么假设使之成为可能?
- 回答:
a. getNumCols()函数会返回 occupantArray[0].length,而这个返回值也就是列的个数;
b. 由于BoundedGrid是个矩阵,所以第一行的列个数也就是整体矩阵的列个数。 - 代码:
- 回答:
//getNumCols()函数:
// @file: GridWorldCode\framework\info\gridworld\grid\BoundedGrid.java
// @line: 53-55
public int getNumCols()
{
return occupantArray[0].length;
}
- 在BoundedGrid中有效的位置有什么要求?
- 回答:BoundedGrid中任意一个点的横坐标要<列数并且>=0,任意一点的纵坐标要<行数并且>=0,即一定要保证点不能越界。
- 代码:
//BoundedGrid的构造函数:
// @file: GridWorldCode\framework\info\gridworld\grid\BoundedGrid.java
// @line: 39-46
public BoundedGrid(int rows, int cols)
在接下来的四个问题中,令r =行数,c =列数,n =占用位置数。
- getOccupiedLocations方法返回什么类型?此方法的时间复杂度(Big-Oh)是多少?
- 回答:
a. 函数返回一个ArrayList集合,这个集合中元素是已经被actors占有的位置;
b. 时间复杂度O(r*c)。 - 代码:
- 回答:
//getOccupiedLocations()函数
// @file: GridWorldCode\framework\info\gridworld\grid\BoundedGrid.java
// @line: 66-83
public ArrayList<Location> getOccupiedLocations()
{
ArrayList<Location> theLocations = new ArrayList<Location>();
// Look at all grid locations.
for (int r = 0; r < getNumRows(); r++)
{
for (int c = 0; c < getNumCols(); c++)
{
// If there's an object at this location, put it in the array.
Location loc = new Location(r, c);
if (get(loc) != null)
theLocations.add(loc);
}
}
return theLocations;
}
- get方法返回什么类型?需要什么参数?此方法的时间复杂度(Big-Oh)是多少?
- 回答:
a. get()函数返回一个E类型的对象;需要的参数是这个对象的Location;
b. 时间复杂度O(1); - 代码:
- 回答:
//get()函数
// @file: GridWorldCode\framework\info\gridworld\grid\BoundedGrid.java
// @line: 85-91
public E get(Location loc)
{
if (!isValid(loc))
throw new IllegalArgumentException("Location " + loc
+ " is not valid");
return (E) occupantArray[loc.getRow()][loc.getCol()]; // unavoidable warning
}
- 哪些条件可能导致put方法引发异常?此方法的时间复杂度(Big-Oh)是多少?
- 回答:
a. 当放置的位置无效或者放置的实体为null时候put()函数会抛出错误;
b. 该方法的时间复杂度O(1); - 代码:
- 回答:
//put()函数
// @file: GridWorldCode\framework\info\gridworld\grid\BoundedGrid.java
// @line: 93-105
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;
}
- remove方法返回什么类型?尝试从空的位置删除项目会发生什么?此方法的时间复杂度(Big-Oh)是多少?
- 回答:
a. remove()函数返回一个E类型的对象;
b. 当移除的是一个无效的位置时候,会抛出一个异常错误;
c. 时间复杂度为O(1); - 代码:
- 回答:
//reMove()函数
// @file: GridWorldCode\framework\info\gridworld\grid\BoundedGrid.java
// @line: 106-117
public E remove(Location loc)
{
if (!isValid(loc))
throw new IllegalArgumentException("Location " + loc
+ " is not valid");
// Remove the object from the grid.
E r = get(loc);
occupantArray[loc.getRow()][loc.getCol()] = null;
return r;
}
- 根据对问题4、5、6和7的回答,您是否认为这是一种有效的实施方式?证明你的答案。
- 回答:我觉得这个实现是有效的。因为当删除,添加,访问某个位置上的某个对象时候,时间复杂度都是O(1)级别的,减少了运算量和内存消耗;同时查询时候是需要遍历的,此时时间复杂度为O(r*c),n^2级别的时间复杂度是可以接受的。因此从这两个维度来说,该实现是比较好的。
- 代码:
我认为该题目不需要用代码以作补充。
Step3:The UnboundedGrid Class
Set 12
- Location类必须实现哪种方法,以便HashMap的实例可以用于map?如果改用TreeMap,Location类将需要什么?Location 是否满足这些要求?
- 回答:
a. Location类中必须实现hashCode()函数与equals()函数,由于Location()使用Compare接口中的方法,所以这个CompareTo()函数也需要实现;
b. 如果要使用TreeMap,那么必须包含Map关键字;
c. Location类满足这些需求; - 代码:
- 回答:
//equals()函数与hashCode函数()
// @file: GridWorldCode\framework\info\gridworld\grid\Location.java
// @line: 218-221 , 205-212 , 234-246
public int hashCode()
{
return getRow() * 3737 + getCol();
}
public boolean equals(Object other)
{
if (!(other instanceof Location))
return false;
Location otherLoc = (Location) other;
return getRow() == otherLoc.getRow() && getCol() == otherLoc.getCol();
}
public int compareTo(Object other)
{
Location otherLoc = (Location) other;
if (getRow() < otherLoc.getRow())
return -1;
if (getRow() > otherLoc.getRow())
return 1;
if (getCol() < otherLoc.getCol())
return -1;
if (getCol() > otherLoc.getCol())
return 1;
return 0;
}
- 为什么对null的检查包含在get,put和remove方法中?为什么BoundedGrid的相应方法中没有包含此类检查?
- 回答:
a. 由于UnboundedGrid对象实例是无界的,所以所有的位置均为有效位置,所以isValid()函数无法发挥作用判断一个位置是否出界;而get,put,remove函数首先需要判断位置是否有效,这在UnboundedGrid对象实例中是不必要的行为,所以这个几个方法也没有被加入到UnboudedGrid类中;
b. 而BoundedGrid对象实例与上面提到的不太一样,这个对象实体是有界的,也就需要我们先去判断位置是否出界,所以在BoundedGrid类中,这几个方法仍然可以使用。 - 代码:
- 回答:
//UnboundedGrid类中isValid()函数
// @file: GridWorldCode\framework\info\gridworld\grid\UnboundedGrid.java
// @line: 53 - 56
public boolean isValid(Location loc)
{
return true;
}
-
三种方法(get、put、remove)的平均时间复杂度(Big-Oh)是多少?如果使用TreeMap代替HashMap会怎样?
-
回答:
a. 平均时间复杂度为 O(1);
b. 如果使用HashMap,时间复杂度将会变为 O(log n);以下是对题目的解析:
在本题中put,get,remove方法都实现需要先找到该实体(也即查询),然后再进行操作;使用TreeMap相当于基于二叉排序树对Grid进行构造,构造的依据是横纵坐标的大小,所以在查询时候也是基于横纵坐标的大小进行查找,在这里的查找过程也近似于二分查找,所以时间复杂度为O(log n);而在使用HashMap时候,直接可以通过下标在O(1)级别的时间复杂度上直接找到该物体,而无需遍历。
-
-
如果使用TreeMap代替HashMap,则除了时间复杂度之外,此类的行为还会有什么不同?
- 回答:访问实例对象的方法也会发生变化。在HashMap方法中,直接通过下标访问对象实例,而在TreeMap中则需要通过树的遍历进行匹配,进而完成对实体对象的访问;
- 代码:
// @file: https://docs.oracle.com/javase/8/docs/api/java/util/TreeMap.html
// @file: https://docs.oracle.com/javase/8/docs/api/java/util/HashMap.html
- 可以将map实现用于BoundedGrid吗?与map实现相比,BoundedGrid类使用的二维数组实现有什么优势(如果有)?
- 回答:
a. 可以被用于BoundedGrid;
b. 当一个Grid存满时候,如果只使用Array不用ArrayList的话,可以免除对实体对象坐标的存储,这样就可以减少一大部分的内存开销。 - 代码:
- 回答:
//@file:https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html
//@file:https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Array.html
set 13
Part5 编程实现问题解答
- 考虑使用HashMap 或 TreeMap实施SparseBoundedGrid。您如何使用UnboundedGrid类完成此任务?UnboundedGrid可以使用哪些方法而无需更改?填写表格,比较每次实施的预期Big-Oh效率SparseBoundedGrid。
令r =行数,c =列数,n =占用位置数
a. SparseBoundedGrid继承UnboundedGrid,所以SparseBoundedGrid实体对象就可以使用UnboundedGrid类中的函数完成相应的功能;
a. get(),put(),remove(),getOccupiedLocations()函数可以仍旧使用
c. 表格
方法 | SparseGridNode 版 | LinkedList 版 | HashMap 版 | TreeMap 版 |
---|---|---|---|---|
getNeighbors | O( c ) | O( c ) | O(1) | O(log n) |
getEmptyAdjacentLocations | O( c ) | O( c ) | O(1) | O(log n) |
getOccupiedAdjacentLocations | O( c ) | O( c ) | O(1) | O(log n) |
getOccupiedLocations | O(r + n) | O(r + n) | O(n) | O(n) |
get | O( c ) | O( c ) | O(1) | O(log n) |
put | O( c ) | O( c ) | O(1) | O(log n) |
remove | O( c ) | O( c ) | O(1) | O(log n) |
- 考虑一个无边界网格的实现,其中所有有效位置的行和列值均为非负值。构造函数分配一个16 x 16的数组。当调用行方法或列索引超出当前数组范围的put方法时,请将两个数组范围加倍,直到它们足够大,使用这些范围构造一个新的正方形数组,然后将现有居住者放入新数组中数组。使用此数据结构实现Grid接口指定的方法。get方法的Big-Oh效率是多少?当行和列索引值在当前数组范围内时,put方法的效率如何?需要调整阵列大小时的效率如何?
回答:
a. get的时间复杂度O(1)
b. 被放置的实体在Grid内时候时间复杂度为O(1);当需要扩展大小时候,时间复杂度为O(n*n),n为新Grid维数的大小。