2021.04,准备面试和CCF CSP认证的我准备作一套CCF模拟题,而后就有了此篇博客(x
题目:201912-2 回收站报数
题目截图:java
第一个想法:读取每一个垃圾的位置,存入TreeSet中,而后依次取出判断是否能够创建回收站和评分(不能够创建回收站,评分为-1)。
这时候就有读者要问了:啊这你为何要使用TreeSet呢?不使用HashSet呢?
我回答:由于输出须要顺序啊,HashSet输出是(没有顺序的)不保证有序的,即有可能出现有序可是程序设计不能依赖于此。
读者:噢,原来如此。
作完题目的我:一看你就是一大段文字不喜欢看(x,题目都没看清(别骂了,我也是🤡),题目最后的输出是须要统计每种得分的个数,不须要有序,用HashSet彻底是足够了。
读者:WTF,一种植物。node
可是在我看错题目的解题过程当中(hhh),出现了错误,准确的说是TreeSet的使用错误,这篇博客记录一下错误和过程。面试
在上述题目中,笔者使用的是TreeSet进行排序,而后每个依次判断输出,可是最后输出的结果所有都是4,全部的垃圾点都是回收站而且评分都是4,即对于任意一个垃圾,周围8个边界都存在垃圾。数组
这是我进行判断而且计算评分的代码:ide
public static int score(int x, int y, Set<Point1912> set) { if (set.contains(new Point1912(x + 1, y)) && set.contains(new Point1912(x - 1, y)) && set.contains(new Point1912(x, y + 1)) && set.contains(new Point1912(x, y - 1))) { int sum = 0; sum += (set.contains(new Point1912(x + 1, y + 1)) ? 1 : 0); sum += (set.contains(new Point1912(x - 1, y + 1)) ? 1 : 0); sum += (set.contains(new Point1912(x + 1, y - 1)) ? 1 : 0); sum += (set.contains(new Point1912(x - 1, y - 1)) ? 1 : 0); return sum; } else { return -1; } }
坐标类函数
class Point1912 { int x = 0; int y = 0; int seq = 0; public Point1912(int x, int y) { this.x = x; this.y = y; } public Point1912(int x, int y, int seq) { this.x = x; this.y = y; this.seq = seq; } @Override public boolean equals(Object obj) { Point1912 p = (Point1912) obj; return x == p.x && y == p.y; } @Override public int hashCode() { return Objects.hash(x, y); } }
TreeSet代码this
TreeSet<Point1912> set1 = new TreeSet<>(((o1, o2) -> o1.seq - o2.seq));
很明显,在判断的代码中任意的set.contains()都返回了true,可是在坐标类中我明确设计了equals和hashCode方法,保证只有坐标相等的时候才能返回true。设计
最终的比较源码,位置在TreeMap.java调试
final Entry<K,V> getEntryUsingComparator(Object key) { @SuppressWarnings("unchecked") K k = (K) key; Comparator<? super K> cpr = comparator; if (cpr != null) { Entry<K,V> p = root; while (p != null) { int cmp = cpr.compare(k, p.key); if (cmp < 0) p = p.left; else if (cmp > 0) p = p.right; else return p; } } return null; }
问题就出如今这里了,TreeSet.contains()方法最终在这里进行比较,重点在于该函数使用的是comparator,这个比较器即是TreeMap用于排序的比较器。由于进行判断的元素seq都是默认值,因此判断是否存在的函数要么所有返回false,要么所有返回true,由于我设置seq默认为0,TreeSet中必定存在因此contains全为true。code
使用HashSet存储坐标,同时使用另外一个ArrayList或者数组存储坐标保证顺序。
下面是HashSet.contains()方法的内容,位置HashMap.java
final Node<K,V> getNode(int hash, Object key) { Node<K,V>[] tab; Node<K,V> first, e; int n; K k; if ((tab = table) != null && (n = tab.length) > 0 && (first = tab[(n - 1) & hash]) != null) { if (first.hash == hash && // always check first node ((k = first.key) == key || (key != null && key.equals(k)))) return first; if ((e = first.next) != null) { if (first instanceof TreeNode) return ((TreeNode<K,V>)first).getTreeNode(hash, key); do { if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) return e; } while ((e = e.next) != null); } } return null; }
能够这里即是采用key.equals的方法进行比较,重写equals起了做用。
同时这个first instance of TreeNode 能够看到HashMap中若是同一个桶的元素超过了7个,在Java1.8中就就将链表转换为了红黑树,Node变成了TreeNode。
TreeMap中元素的比较采用的是Comparator,或者是元素实现的Comparable接口;
HashMap中元素的比较即是元素的equals方法。
若是没看错题目,估计也没这茬子事(嘤嘤嘤x.x)