求边缘点集合问题

今日头条的一道笔试题目
问题描述:
大体是给定点集合(设在第一象限,数量小于50w),设边缘点定义为在点集合中不存在点a其横坐标和纵坐标均大于点x的横纵坐标,这样的点x为边缘点。输出给定点集合的所有边缘点集合(按点的横坐标从小到大排序输出)。

算法:
该开始写这个程序时,由于时间比较紧张,完全没有考虑时间复杂度。所以就使用了蛮力法。
源代码如下:

#include 
#include 
#include 
#include 
using namespace std;
typedef struct Node{
    int x;
    int y;
}NODE,*PNODE;
bool less_first(const NODE & m1, const NODE & m2) {
    return m1.x < m2.x;
}

int main()
{
    int N;
    NODE node;
    vector set1;
    vector result;
    vector::iterator temp, it2;
    cin >> N;
    while (N--)
    {
        cin >> node.x >> node.y;
        set1.push_back(node);
    }
    for (vector::iterator it = set1.begin(); it != set1.end(); it++)
    {
        temp = it;
        for ( it2 = temp++; it2 != set1.end(); it2++)
        {
            if ((*it2).x > (*it).x && (*it2).y > (*it).y)
                break;
        }
        if (it2 == set1.end())
        {
            node.x = (*it).x;
            node.y = (*it).y;
            result.push_back(node);
        }
            //cout << (*it).x << " " << (*it).y << endl;
    }
    sort(result.begin(),result.end(),less_first);
    for (vector::iterator it = result.begin(); it != result.end(); it++)
    {
        cout << (*it).x << " " << (*it).y << endl;
    }
    return 0;
}

后来提交后,发现超时,又开始修改代码,就使用了一种标记手法,如果是属于边缘点集中的点就标记为1,如果不是,则标记为0,新输入的节点只与边缘点集中的点进行比较,判断是否为新边缘点,如果是,则将旧边缘点集中非边缘点集删除(实际上就是更新边缘点集)。提交后只通过了测试用例的20%,其他还是超时。查其原因,实际上算法时间复杂度还是O(n^2)。只有在一般情况下,减小了比较规模而已。

改进源代码1:

#include 
#include 
#include 
#include 
using namespace std;
int a[500000][3];
bool less_cmp(int *p, int *q)
{
    return p[0] < q[0];
}
int main()
{
    int N;
    cin >> N;
    int k = 0;
    for (int i = 0; i < N;i++)
    {
        cin >> a[i][0] >> a[i][1];
        a[i][2] = 1;
        for (int j = 0; j < i; j++){
            if (a[j][2] == 1)
            {
                if (a[i][0]>a[j][0] && a[i][1]>a[j][1])
                {
                    a[j][2] = 0;
                }
                if (a[i][0] < a[j][0] && a[i][1] < a[j][1])
                {
                    a[i][2] = 0;
                }
            }
        }
    }

    sort(a, a + 500000, less_cmp);
    for (int i = 0; i < N; i++)
    {
        if (a[i][2] == 1)
        {
            cout << a[i][0] << " " << a[i][1]<return 0;
}

后来经过同学提示,采用了一种预处理的方法解决。该算法思想如下:
求边缘点集合问题_第1张图片

首先,将给定的点集合以x坐标进行从大到小进行排序预处理。
将第一个点放入边缘点集set中,并将temp设为第一个点的纵坐标y;
从x坐标从大到小进行一次循环遍历点集
判断点的纵坐标y’是否大于temp;
如果大于,则将temp设为y’,并将该点放入边缘点集set中;
否则,进行下一轮循环。
循环结束。
将边缘点集以横坐标x从小到大排序,然后输出。

算法复杂度:分析该算法,只有一层循环,时间复杂度为O(n)。提交能满足要求。

改进源代码2:

#include 
#include 
#include 
#include 
using namespace std;
typedef struct Node{
    int x;
    int y;
}NODE, *PNODE;
bool big_cmp(const NODE & m1, const NODE & m2) {
    return m1.x > m2.x;
}
bool less_cmp(const NODE & m1, const NODE & m2) {
    return m1.x < m2.x;
}
int main()
{
    int N;
    NODE node;
    vector set1;
    vector result;
    int temp;
    vector::iterator  it2;
    cin >> N;
    while (N--)
    {
        cin >> node.x >> node.y;
        set1.push_back(node);
    }
    //将输入节点集以横坐标x从大到小排序
    sort(set1.begin(), set1.end(), big_cmp);
    //循环遍历,判断是否为边缘点,如果是则加入到结果集中,否则继续。
    for (vector::iterator it = set1.begin(); it != set1.end(); it++)
    {
        if (result.empty())
        {
            temp = (*it).y;
            result.push_back(*it);
        }
        else if (temp < (*it).y)
        {
            temp = (*it).y;
            result.push_back(*it);
        }
        else
            continue;
    }

    //将边缘点集排序,然后循环输出
    sort(result.begin(), result.end(), less_cmp);
    for (vector::iterator it = result.begin();it != result.end();it++)
    {
        cout << (*it).x << " " << (*it).y << endl;
    }
    return 0;
}

测试结果:提交后,通过!

你可能感兴趣的:(算法)