Skip to content

Instantly share code, notes, and snippets.

@KirillLykov
Last active February 14, 2020 09:59
Show Gist options
  • Save KirillLykov/e4f25c360b230298f594752216192d90 to your computer and use it in GitHub Desktop.
Save KirillLykov/e4f25c360b230298f594752216192d90 to your computer and use it in GitHub Desktop.
[Geometry] Smallest rectangle in cloud of points

The problem is to find all rectangular of minimal area spanned on 4 points from the given set. See lc.

The brute force solution is to have a cache of already tackled points and for each current point and for each point in cache we are looking in the cache if necessary 2 other points existed. Beside of fast search, the cache of seen points also allows to avoid considering dublicates.

    struct Hash {
        size_t operator()(const pair<int,int>&x)const{
            return hash<long long>()(((long long)x.first)^(((long long)x.second)<<32));
        }
    };
    // solution 1 - 416ms, 16MB
    int minAreaRect(vector<vector<int>>& points) {
        unordered_set<pair<int,int>, Hash> seen;
        int res = numeric_limits<int>::max();
        for (const auto& p : points) {
            int x1 = p[0], y1 = p[1];
            for (auto[x2,y2] : seen) 
                if (seen.count({x1, y2}) && seen.count({x2, y1})) {
                    int area = abs(x1 - x2)*abs(y1 - y2);
                    res = min(area, res);
                }
            
            seen.emplace(x1, y1);
        }
        return res == numeric_limits<int>::max() ? 0 : res;
    }

For the second solution, we sort points by x from left to right (achieved by using map) and traverse through x coordinate. For each x, we have a set of ys. For each pair in this set we need to search a matching pair on the left. The condition j < i is just to avoid considering dublicates. So, one more time, if the current pair of ys is y1, y2 we need to find exactly the same pair of ys with different x in the past. Moreover, it is enough to consider the closest in the past pair of y1, y2 because if there exists a pair erlier it gives larger rectangle.

    // Solution 2
    pair<int,int> count(vector<vector<int>>& points) {
        unordered_set<int> xs, ys;
        for (auto& p : points) {
            xs.insert(p[0]);
            ys.insert(p[1]);
        }
        return {xs.size(), ys.size()};
    }
    
    int minAreaRect(vector<vector<int>>& points) {
        // optimization - count distinct x and y
        const auto&[nx, ny] = count(points);
        if (nx == 0 || ny == 0) return 0; // gives -20ms
        
        // this optimization gives total runtime improvement x10
        map<int, vector<int>> x2y;
        if (nx > ny)
            for (auto& p : points)
                x2y[p[0]].emplace_back(p[1]);
        else
            for (auto& p : points)
                x2y[p[1]].emplace_back(p[0]);
        
        unordered_map<pair<int,int>, int, Hash> seenPairsOfY2X;
        int res = numeric_limits<int>::max();
        for (auto&[x, ys] : x2y) {
            sort(ys.begin(), ys.end());
            for (int i = 0; i < ys.size(); ++i)
                for (int j = 0; j < i; ++j) {
                    int y1 = ys[j], y2 = ys[i]; // y2 > y1
                    if (seenPairsOfY2X.count({y1,y2})) {
                        int x1 = seenPairsOfY2X[{y1,y2}];
                        int x2 = x;
                        int area = (y2 - y1) * (x2 - x1);
                        res = min(res, area);
                    }
                    seenPairsOfY2X[{y1,y2}] = x;
                }
        }
        return res == numeric_limits<int>::max() ? 0 : res;   
    }
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment