1.创建一种数据结构,使得如下操作时间复杂度为O(1):
boolean insert(int val) 如果插入结果已存在,则返回false,反之为true。
boolean remove(int val) 要删除的元素存在,则返回true,反之为false。
int getRandom() 返回集合中任意元素。概率为该元素出现的次数/元素数总和。
分析:插入要在O(1)之内,很容易。getRandom想要在O(1)时间内,按照元素出现的频率来作为概率,则必须是将所有元素放置(重复元素重复放置)在一维数组中,才能做到。因此,解决删除问题,才是要点。
考虑如下矩阵:
[10,10,20,20,30,30]
当删除10时,删除了a[1]位置的10,然后将元素末尾a[5]的30,填充到a[1]
填充后的矩阵
[10,30,20,20,30]
这样,就可以满足getRandom的需求,也能在O(1)时间内完成。
观察如上操作,发现,需要记录每一个元素的下标,每当删除元素时,将集合末尾的元素,直接移动到删除元素的下标处。考虑到重复元素,下标应该是一个集合,而且,这个集合要不断的根据下标值删除,(上例中,就要删除30的下标集合中为5的下标)。为了使这一步时间复杂度为O(1),则只能使用hash。
public class RandomizedCollection {
private Map<Integer, Set<Integer>> map;
private ArrayList<Integer> arrayList;
/** Initialize your data structure here. */
public RandomizedCollection() {
map = new HashMap<>();
arrayList = new ArrayList<>();
}
/**
* Inserts a value to the collection. Returns true if the collection did not
* already contain the specified element.
*/
public boolean insert(int val) {
boolean mark = false;
if (!map.containsKey(val)) {
Set<Integer> set = new HashSet<>();
map.put(val, set);
mark = true;
}
map.get(val).add(arrayList.size());
arrayList.add(val);
return mark;
}
/**
* Removes a value from the collection. Returns true if the collection
* contained the specified element.
*/
public boolean remove(int val) {
HashSet<Integer> set = new HashSet<>();
set.remove(0);
if (!map.containsKey(val)) {
return false;
}
int indexOfVal = map.get(val).iterator().next();
map.get(val).remove(indexOfVal);
if (map.get(val).size() == 0)
map.remove(val);
// 如果删除的不是尾部元素
if (indexOfVal != arrayList.size() - 1) {
int indexLast = arrayList.size() - 1;
int elemLast = arrayList.get(indexLast);// 获取最后一个元素
arrayList.set(indexOfVal, elemLast);// 将该元素填补到被删除元素的空白处。
map.get(elemLast).remove(indexLast);
map.get(elemLast).add(indexOfVal);
}
arrayList.remove(arrayList.size() - 1);
return true;
}
/** Get a random element from the collection. */
public int getRandom() {
return arrayList.get(new Random().nextInt(arrayList.size()));
}
}
2.小矩形组成大矩形
Given N axis-aligned rectangles where N > 0, determine if they all together form an exact cover of a rectangular region.
Each rectangle is represented as a bottom-left point and a top-right point. For example, a unit square is represented as [1,1,2,2]. (coordinate of bottom-left point is (1, 1) and top-right point is (2, 2)).
分析:最后的大矩形,其最左下角的坐标是矩形集合中的最左下坐标,其右上亦如此。只要获取到这两个坐标,矩阵就唯一确定。大矩形的面积,必然等于所有小矩形的面积。
有一种特殊情况:重合的面积正好补齐了残缺的面积。仔细观察大矩形可知,除去四个顶点处的点外,其他小矩形的点都有偶数个。将每个小矩形的四个点放入set集合中,每当遇到重复的则删除,最后剩下的必然是四个顶点处的点。
public boolean isRectangleCover(int[][] rectangles) {
if (null == rectangles || 0 > rectangles.length || 4 != rectangles[0].length) {
return false;
}
int xLeft = Integer.MAX_VALUE;
int yLeft = Integer.MAX_VALUE;
int xRight = Integer.MIN_VALUE;
int yRight = Integer.MIN_VALUE;
int areaSum = 0;
Set<String> set = new HashSet<>();
for (int i = 0; i < rectangles.length; i++) {
xLeft = Math.min(xLeft, rectangles[i][0]);
yLeft = Math.min(yLeft, rectangles[i][1]);
xRight = Math.max(xRight, rectangles[i][2]);
yRight = Math.max(yRight, rectangles[i][3]);
areaSum += (rectangles[i][2] - rectangles[i][0]) * (rectangles[i][3] - rectangles[i][1]);
// 将四个点添加到set集合中
addPoint(rectangles[i][0] + "" + rectangles[i][1], set);
addPoint(rectangles[i][0] + "" + rectangles[i][3], set);
addPoint(rectangles[i][2] + "" + rectangles[i][1], set);
addPoint(rectangles[i][2] + "" + rectangles[i][3], set);
}
int newArea = (xRight - xLeft) * (yRight - yLeft);
if (newArea != areaSum) {
return false;
} else {
if (set.size() == 4 && set.contains(xLeft + "" + yLeft) && set.contains(xLeft + "" + yRight)
&& set.contains(xRight + "" + yLeft) && set.contains(xRight + "" + yRight)) {
return true;
} else {
return false;
}
}
}
private void addPoint(String key, Set<String> set) {
if (set.contains(key)) {
set.remove(key);
} else {
set.add(key);
}
}