LeetCode 391. Perfect Rectangle
方法一:Line Sweep
naive的想法是检查面积是否相等,但可以举出overlap的反例。顺着这种思路想,如果能判断是否重叠,那么本题就解决了。
暴力检查重叠太慢了。这里采用line sweep的方法。先根据x排序,维护一个对于y interval的active set。从左向右扫描,遇到左边界检查是否有重叠后insert进set,右边界则从set中erase。interval 重载了 < 符号。
注意如果x相等,右边界应该先处理,因为这种情况下不算overlap。如果左边界先处理,就会判断为overlap。
class Solution { public: // [y1,y2] for rectangles struct interval { int start, end; interval(int s, int e):start(s),end(e){}; bool operator < (const interval &other) const { return start < other.start; } }; // left or right edge of rectangles struct edge { int x; interval i; int type; // left <- 1, right <- -1 }; bool isRectangleCover(vector<vector<int>>& rectangles) { vector<edge> vec; int minx = INT_MAX, miny = INT_MAX, maxx = INT_MIN, maxy = INT_MIN; int area=0; for (const auto &rect:rectangles){ area += (rect[2]-rect[0])* (rect[3]-rect[1]); minx = min(rect[0], minx); miny = min(rect[1], miny); maxx = max(rect[2], maxx); maxy = max(rect[3], maxy); vec.push_back({rect[0],{rect[1],rect[3]},1}); vec.push_back({rect[2],{rect[1],rect[3]},-1}); } sort(vec.begin(),vec.end(),[](edge e1, edge e2){ if (e1.x==e2.x) return e1.type<e2.type; return e1.x<e2.x; }); set<interval> active_intervals; for (edge tmp:vec){ interval i=tmp.i; int type=tmp.type; if (type==1){ // left edge, insert interval and check auto it=active_intervals.lower_bound(i); if (it!=active_intervals.begin() && prev(it)->end > i.start) return false; if (it!=active_intervals.end() && i.end > it->start) return false; active_intervals.insert(it, i); }else{ active_intervals.erase(i); } } return area == (maxx-minx) * (maxy - miny); } };
时间复杂度 O(nlogn)
https://leetcode.com/problems/perfect-rectangle/discuss/87196/clean-C++-sweep-line-solution
方法二:Corner Count
上述方法在check面积的同时,还检测了是否存在overlap。我们也可以检查每个corner出现的次数,如果在面积相等的情况下能组成矩形,那么奇数的corner有且仅能有四个:四个顶点。别的corner因为重合,必定是偶数个。
需要注意的是,pair没法直接hash,因此需要自定义hash函数。为了方便起见,这里用set,pair是先根据x,再根据y排序。
Why can't I use pair as key of unordered_set / unordered_map?
class Solution { public: bool isRectangleCover(vector<vector<int>>& rectangles) { set<pair<int,int>> corners; int area=0; for (auto &rect:rectangles){ vector<pair<int,int>> coords; coords.push_back({rect[0],rect[1]}); coords.push_back({rect[2],rect[1]}); coords.push_back({rect[0],rect[3]}); coords.push_back({rect[2],rect[3]}); for (auto coord:coords){ if (corners.count(coord)) corners.erase(coord); else corners.insert(coord); } area += (rect[2]-rect[0]) * (rect[3]-rect[1]); } if (corners.size()!=4) return false; auto bottomleft=corners.begin(); auto topright=corners.rbegin(); return area == (topright->first-bottomleft->first) * (topright->second-bottomleft->second); } };
时间复杂度:set O(nlogn),如果自定义hash函数,用unordered_set,O(n)