2017头条笔试题:二维点集中找出右上角没有点的点并按x坐标从小到大打印坐标

PS:这篇是之前本来就想发的但是一直没时间写,加上今天做了京东的题,结果代码名就命名为jingdong了……懒得改代码名重新跑一遍结果了=。=

暴力法去做就是遍历每个点,判断它是不是“最大点”。判断过程则是又遍历一遍,看看是否存在其他点在它右上方,若存在则不是最大点。O(N^2)

但是这样就会有很多不必要的计算,举个例子(这里暂且当坐标都是int),若存在一个最大点(x0,y0),那么所有在它左下角的点都不用考虑了。

另外,对于(x0,y0),只需要查找在它右边(x>x0)的点是否在它上面。如果预先将点根据x坐标排序,那么判断过程就从for i in [0, n)变成了for i in [x0, n),但这并没有本质的提升,还是O(N^2)。——这也暗示了点集中最右边的点必然是最大点。

然后再注意,如果右边的点存在一个点满足y>y0,那么判断就会返回false了;若不存在则判断返回true。

关键就是记录右边的点的ymax,不必每次都遍历一遍重复计算ymax。

到了这一步后就可以写代码了,注意,由于输入并不是有序的,必须得经过预处理(按照x排序),坐标轴范围是0~1e9的话,用位图法排序(参考编程珠玑)时间复杂度还是1e9的数量级,原题数据量不超过100000,位图法排序并不比快排快。

#include <algorithm>
#include <limits.h>
using namespace std;

struct Point
{
    int x, y;
};

// a[n]为从输入中读取得到的点集数组
void solution(Point* a, int n)
{
    std::sort(a, a + n, [](Point& p1, Point& p2) { return p1.x < p2.x; });
    int ymax = INT_MIN;  // 记录从右往左遍历过程中y的最大值
    for (int i = n - 1; i >= 0; i--)
    {
        if (a[i].y > ymax)  // 此时a[i].y大于或等于右边所有点的最大y坐标, a[i]为最大点
        {
            printf("%d %d\n", a[i].x, a[i].y);
            ymax = a[i].y;  // 更新最大y坐标
        }
    }
}

上述代码的成功有个前提,也是题目里的限制:【所有点的横纵坐标都不重复】

这点必须注意!如果没有这个限制,比如y坐标不重复,比如对点集(1,1) (2,3) (3,3) (4,2),按照上面的做法只会得出(3,3)和(4,2)两个最大点,但是(2,3)其实也是最大点。

错误的地方在于判断语句:a[i].y > ymax,如果该点的y坐标与ymax相等,那么该点也是最大点.。

因此判断语句要改成

if (a[i].y >= ymax)

如果连x坐标不重复这个限制都没有的话,那就更复杂,比如序列经过sort排序后为(1,3)(1,4)(2,2),(2,2)和(1,4)被确认为最大点,但是判断(1,3)时,由于当前ymax已经被更新为4了,(1,3)不会被当成最大点。

这样一来,逻辑就变成了【对所有满足x=x0的点,其中y>=ymax的点都是最大点】,关键是要找出满足x=x0的点集区间。

在没有【所有的横纵坐标都不重复】这个限制下的完整代码与简单测试结果如下

#include <iostream>
#include <vector>
#include <algorithm>
#include <limits.h>
using namespace std;

struct Point
{
    int x, y;
    Point(int x_, int y_) : x(x_), y(y_) { }
    bool operator<(const Point& rhs) const
    {
        return x < rhs.x;
    }
};

void solution(Point* a, int n)
{
    vector<Point> res;
    res.reserve(n);

    // 注释掉排序预处理的代码, 输入排序后的结果进行测试
    // 因为快排不是稳定排序, 可能打乱x相同的若干点之间的相对顺序
//    sort(a, a + n);
    int ymax = INT_MIN;  // 记录从右往左遍历过程中y的最大值

    Point* low = &a[n - 1];
    Point* high = &a[n];

    while (low >= a)
    {
        // 寻找x坐标相同的左闭右开区间
        while (low > a)  // 保证low-1在a[n]中
        {
            auto it = low - 1;
            if (it->x == low->x)
                low = it;
            else
                break;
        }
        // 左闭右开区间[low, high)的点的x坐标相同
        // 区间内所有y不小于ymax的点均为最大点
        int temp = ymax;
        for (auto it = low; it != high; ++it)
        {
            if (it->y >= ymax)
            {
                res.emplace_back(*it);
                if (it->y > temp)  // 同时求出[low, a+n)区间的最大y坐标
                    temp = it->y;
            }
        }
        ymax = temp;
        high = low;
        low = high - 1;
    }

    // 按照x坐标从小到大输出结果
    std::sort(res.begin(), res.end());
    for (auto& pt : res)
        cout << pt.x << " " << pt.y << endl;
}

int main()
{
    // 读取输入
    int n;  // 点数
    cin >> n;
    vector<Point> v;
    v.reserve(n);
    for (int i = 0; i < n; i++)
    {
        int x,y;
        cin >> x >> y;
        v.emplace_back(x, y);
    }

    // 计算并输出结果
    solution(&v[0], v.size());
    return 0;
}

测试用例data1是针对特殊情况(x/y坐标有重复的)的用例,data2是题目的示意图的用例

[email protected]:~/Algorithms/2017$ g++ jingdong.cpp -std=c++11
[email protected]:~/Algorithms/2017$ cat data1
4
1 2
1 3
1 4
2 3
[email protected]:~/Algorithms/2017$ cat data1 | ./a.out
1 3
1 4
2 3
[email protected]:~/Algorithms/2017$ cat data2
9
1 10
2 3
3 8
4 4
5 6
5 3
6 9
7 7
8 5
[email protected]:~/Algorithms/2017$ cat data2 | ./a.out
1 10
6 9
7 7
8 5

如果是在题目限制下的算法,对data1的结果如下(漏掉了(1,3)这个点)

[email protected]:~/Algorithms/2017$ g++ jingdong_old.cpp -std=c++11
[email protected]:~/Algorithms/2017$ cat data1 | ./a.out
1 4
2 3
时间: 2024-08-09 02:05:57

2017头条笔试题:二维点集中找出右上角没有点的点并按x坐标从小到大打印坐标的相关文章

笔试题--在一个整数数组中找出第5大的数

/** * 在一个整数数组中找出第5大的数 * 思路是首先在数组中找到最小数,然后依次找到第五大的数 * * @param array * @return */ public static int[] selectionSort(int[] array) { if (array.length == 0) return array; for (int i = 0; i < array.length; i++) { int minIndex = i; for (int j = i; j < arr

【刷题】二维数组中找数字

一.描述 在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的排序排序,请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. 二.解题思路 关键:从数组的最右上方的数开始判断,根据该数与指定整数的大小进行下一步的比较. 三.代码 根据思路可以得到如下的代码 public class FindInPartiallySortedMatrix { public static void main(String[] args) { int[][] mat

面试题-10亿个数中找出最大的10000个数(top K问题)

一个较好的方法:先拿出10000个建立小根堆,对于剩下的元素,如果大于堆顶元素的值,删除堆顶元素,再进行插入操作,否则直接跳过,这样知道所有元素遍历完,堆中的10000个就是最大的10000个.时间复杂度: m + (n-1)logm = O(nlogm) 优化的方法:可以把所有10亿个数据分组存放,比如分别放在1000个文件中(如果是字符串hash(x)%M).对每个文件,建立大小为10000的小根堆,然后按有序数组的合并合并起来,取出最大的10000个即是答案. top K问题 在大规模数据

剑指offer 面试题(二维数组中的查找) (2)

面试题: 二维数组中的查找 /* 题目:   在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成 一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该数. */ ps:(其实前段时间我就做过这道题,今天看到了,觉得还是有点生,那就再来一次吧) 题目分析:  在分析这个问题的时候,我们首先要看,在一个二维数组中查找一个数字是否存在,那么很多人就觉得 简单了,遍历二维数组与所需要查找的数字进行比较不就完了!  不可否认的是你说的是可行的,可是

【剑指Offer面试题】二维数组中的查找

下决心AC所有剑指offer面试题. 九度OJ面试题地址:http://ac.jobdu.com/hhtproblems.php 书籍:何海涛--<剑指Offer:名企面试官精讲典型编程题> 对于面试题,面试官往往更希望我们能提出优化方法,这样更能体现我们的思维能力以及传说中的"内功".所以做剑指offer要着重训练这方面,多总结多细究,总是有好处的.加油~ 二维数组中的查找 时间限制:1 秒内存限制:32 兆 特殊判题:否提交:19005解决:3642 题目描述: 在一个

【剑指Offer面试题】九度OJ1384:二维数组中的查找

下决心AC全部剑指offer面试题. 九度OJ面试题地址:http://ac.jobdu.com/hhtproblems.php 书籍:何海涛--<剑指Offer:名企面试官精讲典型编程题> 对于面试题,面试官往往更希望我们能提出优化方法,这样更能体现我们的思维能力以及传说中的"内功".所以做剑指offer要着重训练这方面,多总结多细究,总是有优点的.加油~ 题目链接地址: http://ac.jobdu.com/problem.php?pid=1384 二维数组中的查找

面试题1: 二维数组中查找

本题出自<剑指offer 名企面试官精讲典型编程题>面试题3. 题目3:在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列按照从上到下递增的顺序排序.请完成一个函数,输入一个这样的二维数组和整数,判断数组中是否含有该整数. 例如下面的二维数组就是每行每列递增排序.如果在数组中查询7,则返回true:如果查找数字14,由于数组中不包含14,则返回false. 解决方法分析: 首先我们选取二维数组左下角的数字8,由于8大于7,并且8还是第四行的第一个数,因此数字7不可能出现在8所在的行,

《剑指Offer》面试题:二维数组中的查找

题目 题目:在一个二维数组中,每一行都按照从左到右的顺序进行了排序,每一列都按照从上到下进行了排序. 输入这样的一个二维数组arr和一个整数k,判断数组中是否存在这个数. 思路 由于数组时有序的这样一个前提,因此 从右上角开始查找 :如果要查找的数比右上角的值大,则查找范围删除掉(并不是实际的删除,只需要改变下标即可)这一行,如果要查找的数比右上角的值小,则查找范围删除掉这一列:这样就可以将查找范围逐渐缩小,直至完成搜索 当然也可以从右下角开始查找,但是不可以从左上角或者是左下角开始查找(因为这

【剑指offer】面试题 4.二维数组中的查找

面试题 4. 二维数组中的查找 题目:在一个二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序. 请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数. 举例 例如下面的二维数组就是每行.每列都递增排列.如果在这个数组中查找数字7,则返回true;如果查找数字5,由于 数组不含有该数字,则返回false. 1 2 8 9 2 4 9 12 4 7 10 13 6 8 11 15 复杂度:O(M + N) + O(1) 代码实现 public