力扣:218. 天际线问题

143 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第21天,点击查看活动详情

218. 天际线问题 - 力扣(LeetCode)

城市的 天际线 是从远处观看该城市中所有建筑物形成的轮廓的外部轮廓。给你所有建筑物的位置和高度,请返回 由这些建筑物形成的 天际线 。

每个建筑物的几何信息由数组 buildings 表示,其中三元组 buildings[i] = [lefti, righti, heighti] 表示:

  • lefti 是第 i 座建筑物左边缘的 x 坐标。
  • righti 是第 i 座建筑物右边缘的 x 坐标。
  • heighti 是第 i 座建筑物的高度。

你可以假设所有的建筑都是完美的长方形,在高度为 0 的绝对平坦的表面上。

天际线 应该表示为由 “关键点” 组成的列表,格式 [[x1,y1],[x2,y2],...] ,并按 x 坐标 进行 排序 。关键点是水平线段的左端点。列表中最后一个点是最右侧建筑物的终点,y 坐标始终为 0 ,仅用于标记天际线的终点。此外,任何两个相邻建筑物之间的地面都应被视为天际线轮廓的一部分。

注意:输出天际线中不得有连续的相同高度的水平线。例如 [...[2 3], [4 5], [7 5], [11 5], [12 7]...] 是不正确的答案;三条高度为 5 的线应该在最终输出中合并为一个:[...[2 3], [4 5], [12 7], ...]

merged.jpg (1924×796) (leetcode.com)

示例 1:
输入:buildings = [[2,9,10],[3,7,15],[5,12,12],[15,20,10],[19,24,8]]
输出:[[2,10],[3,15],[7,12],[12,0],[15,10],[20,8],[24,0]]
解释:
图 A 显示输入的所有建筑物的位置和高度,
图 B 显示由这些建筑物形成的天际线。图 B 中的红点表示输出列表中的关键点。
​
示例 2:
输入:buildings = [[0,2,3],[2,5,3]]
输出:[[0,3],[5,0]]

提示:

  • 1 <= buildings.length <= 10^4
  • 0 <= lefti < righti <= 2^31 - 1
  • 1 <= heighti <= 2^31 - 1
  • buildings 按 lefti 非递减排序

问题解析

这题的要求很简单,就是让我们先找出每个位置最高的高度,然后输出所有高度变化处的点。

主要难点就在于怎么知道每个位置的最高高度。

  1. 我们可以把每个房子,看做给区间lefti到righti处都赋值为heighti ,若一个位置被重复赋值,我们取最大值。
  2. 这一点我们可以用区间赋值和单点查询的线段树来做到。
  3. 赋值完后,对于每个位置i,我们在线段树上查询出他的最大值。把结果存在数组里。
  4. 然后遍历一遍数组,对于每个变化的位置,我们把它和当前的高度都记录下来。

要注意的是:

  1. 因为left和right过大,但n的总数很小,所以我们要对left和right离散化。
  2. 由于线段树的特性,对于一个房子,它实际赋值的区间应该为left和right-1。

AC代码

typedef long long ll;
const int N=3e4+50;
class Solution {
public:
    ll f[4*N],lz[4*N],a[N];
    void push_down(int k)
    {
        if(lz[k])
        {
            lz[k+k]=max(lz[k+k],lz[k]);
            lz[k+k+1]=max(lz[k+k+1],lz[k]);
            f[k+k]=max(f[k+k],lz[k]);
            f[k+k+1]=max(f[k+k+1],lz[k]);
            lz[k]=0;
        }
    }
    void revise(int k,int l,int r,int x,int y,ll z)
    {
        if(l==x&&r==y)
        {
            lz[k]=max(lz[k],z);
            f[k]=max(f[k],z);
            return;
        }
        push_down(k);
        int mid=(l+r)/2;
        if(y<=mid)revise(k+k,l,mid,x,y,z);
        else 
            if(x>mid)revise(k+k+1,mid+1,r,x,y,z);
            else 
            {
                revise(k+k,l,mid,x,mid,z);
                revise(k+k+1,mid+1,r,mid+1,y,z);
            }
    }
    int query(int k,int l,int r,int x)
    {
        if(l==r)return f[k];
        push_down(k);
        int mid=(l+r)/2;
        if(x<=mid)return query(k+k,l,mid,x);
        else return query(k+k+1,mid+1,r,x);
    }
    vector<vector<int>> getSkyline(vector<vector<int>>& buildings) {
        unordered_map<ll,int>mymap,mymap2;
        vector<int>v;
        for(auto i:buildings)
        {
            v.push_back(i[0]);
            v.push_back(i[1]-1);
            v.push_back(i[1]);
        }
        int cnt=0;
        sort(v.begin(),v.end());
        for(auto i:v)
        {
            if(!mymap.count(i))
            {
                mymap[i]=++cnt;
                mymap2[cnt]=i;
            }
        }
        for(auto &i:buildings)
        {
            revise(1,1,cnt,mymap[i[0]],mymap[i[1]-1],i[2]);
        }
        vector<int>ans(cnt+1);
        for(int i=1;i<=cnt;i++)
        {
            ans[i]=query(1,1,cnt,i);
        }
        vector<vector<int>>res;
        ll mx=0;
        for(int i=0;i<=cnt;i++)
        {
            if(ans[i]!=mx)
            {
                mx=ans[i];
                res.push_back({mymap2[i],ans[i]});
            }
        }
        return res;
    }
};