# 给定二维平面整数点集输出“最大点集”算法（今日头条面试题）

### 题目

P为给定的二维平面整数点集。定义 P 中某点x，如果x满足 P 中任意点都不在 x 的右上方区域内（横纵坐标都大于x），则称其为“最大的”。求出所有“最大的”点的集合。（所有点的横坐标和纵坐标都不重复, 坐标轴范围在[0, 1e9) 内）

##### 输入例子1:
5
1 2
5 3
4 6
7 5
9 0
##### 输出例子1:
4 6
7 5
9 0

### 思路

1.暴力搜索法

2.变治法（预排序）

3.减治法+变治法（过滤+预排序）

4.空间分割（优化的四叉树）

build tree cost: 167ms
105233923 999996852
398502327 999994996
837309014 999994263
899779160 999993102
980746971 999976098
990941053 999881685
991773349 999539486
996437667 999536388
999934209 999456481
999948454 989946068
999951793 933115039
999993165 920862637
999996248 471725091
search tree cost: 106ms

int x=999999991;

float y = 1000000000f;

float z = x/y;

using System.IO;
using System;
using System.Collections.Generic;
using System.Diagnostics;
class Program
{
private static int maxData = 1000000000;
private static List<MyPoint> dataPoints = new List<MyPoint>();
private static Random rand = new Random();
static void Main()
{

float time = 0;
Stopwatch watch = new Stopwatch();
watch.Start();

for (int i = 0; i < count; i++)
{
MyPoint temp;
//string[] inputs = Console.ReadLine().Split(new char[] { ' ' });
//temp.x = Convert.ToInt32(inputs[0]);
//temp.y = Convert.ToInt32(inputs[1]);
temp.x = rand.Next(maxData);
temp.y = rand.Next(maxData);
}
time = watch.ElapsedMilliseconds - time;
Console.WriteLine("build tree cost: " + time + "ms");
List<MyPoint> result = new List<MyPoint>();
Rectangle rect;
rect.width = rect.height = maxData + 1;

for (int i = 0; i < count; i++)
{

rect.x = dataPoints[i].x;
rect.y = dataPoints[i].y;
{
continue;
}

}
//要以x轴y从小到大输出，所以结果集需要排序
result.Sort();
for(int i=0;i< result.Count; i++)
{
Console.WriteLine( result[i]);
}
time = watch.ElapsedMilliseconds - time;
Console.WriteLine("search tree cost: " + time + "ms");
watch.Stop();
}
}

{
{
public int maxLevel;
public double maxWidth;
public double maxHeight;
{
this.maxLevel = maxLevel;
this.maxWidth = maxWidth;
this.maxHeight = maxHeight;

int maxNodes = 0;
for (int i = 0; i <= maxLevel; i++)
{
maxNodes += (int)Math.Pow(4, i);
}

}
}

private int level;
private int parent;
private int count;
private List<MyPoint> points;
private Rectangle bounds;

{
this.bounds = bounds;
level = 0;
count = 0;
parent = -1;
Init();
}

private void Init()
{

data.allNodes[0] = this;

for (int i = 0; i < data.allNodes.Length; i++)
{
if (data.allNodes[i].level >= data.maxLevel)
break;
InitChildrenNew(i);
}

}
private void InitChildrenNew(int parentIndex)
{

Rectangle bounds = data.allNodes[parentIndex].bounds;
float subWidth = (bounds.getWidth() / 2);
float subHeight = (bounds.getHeight() / 2);
float x = bounds.getX();
float y = bounds.getY();

int nextLevel =  data.allNodes[parentIndex].level + 1;
byte[,] offset =new byte[,]{{0,0},{1,0},{0,1},{1,1}};
for (int i = 0; i < 4; i++)
{
Rectangle rect = new Rectangle(x,y,subWidth,subHeight);

rect.x += offset[i,0]*subWidth;
rect.y += offset[i,1]*subHeight;

int childIndex = GetPointIndexByLevel(rect.getCenter(), nextLevel);
if (childIndex < data.allNodes.Length)
{
data.allNodes[childIndex] = new Quadtree(nextLevel, rect, data);
data.allNodes[childIndex].parent = parentIndex;
data.allNodes[parentIndex].nodes[i] = data.allNodes[childIndex];
//Console.WriteLine("p:"+parentIndex+",c:"+childIndex+",size:"+ rect.width);
}

}
}

{
level = pLevel;
bounds = pBounds;
count = 0;
data = pData;
}

public int GetPointIndexByLevel(MyPoint point, int targetLevel)
{

int[] indexByLevel={0,1,5,21,85,341,1365,5461,21845};
int startIndex =indexByLevel[targetLevel] ;

int cc = (int)Math.Pow(2, targetLevel);

//if(point.x >= data.maxWidth || point.y >=data.maxHeight)
//{
//    Console.WriteLine("error point:"+point);
//    Console.WriteLine("data:"+data.maxWidth+","+data.maxHeight);
//}
int locationX = (int)(point.x / data.maxWidth * cc);
int locationY = (int)(point.y / data.maxHeight * cc);
int idx = startIndex + locationY * cc + locationX;

return idx;
}
/*
* Insert the object into the quadtree. If the node
* exceeds the capacity, it will split and add all
* objects to their corresponding nodes.
*/
public void Insert(MyPoint point)
{

int idx = GetPointIndexByLevel(point, data.maxLevel);

{

}

}
{
if(parent >=0 )
{
var nodeParent = data.allNodes[parent];
}
count++;
}

/*
* Return all objects that could collide with the given object
*/
public bool retrieve(Rectangle pRect)
{

if(count > 0 && pRect.Contains(bounds))
{
return true;
}

if(count > 0 && bounds.Intersects(pRect))
{

if (points != null)
{

for (int i = 0; i < points.Count; i++)
{

if (pRect.Contains(points[i]))
{
return true;
}

}
}

else if (level < data.maxLevel)
{

if (nodes[3] != null && nodes[3].retrieve(pRect)) return true;
if (nodes[2] != null && nodes[2].retrieve(pRect)) return true;
if (nodes[1] != null && nodes[1].retrieve(pRect)) return true;
if (nodes[0] != null && nodes[0].retrieve(pRect)) return true;
}

}

return false;

}

}

public struct MyPoint : IComparable<MyPoint>
{
public int x;
public int y;
public MyPoint(int x = 0, int y = 0)
{
this.x = x;
this.y = y;
}

public override string ToString()
{
return  x + " " + y;
}
public int CompareTo(MyPoint other)
{
if(x == other.x)
return 0;
else if(x > other.x)
return 1;
else if( x < other.x)
return -1;

return -1;
}
}
public struct Rectangle
{
public float x;
public float y;
public float height;
public float width;
public Rectangle(float x = 0, float y = 0, float width = 0, float height = 0)
{
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
public float getX() { return x; }
public float getY() { return y; }
public float getHeight() { return height; }
public float getWidth() { return width; }
public MyPoint getCenter() { return new MyPoint((int)(x + width / 2), (int)(y + height / 2)); }
public bool Intersects(Rectangle Rect)
{
return (!(y > Rect.y + Rect.height ||
y + height < Rect.y ||
x + width < Rect.x ||
x > Rect.x + Rect.width));
}
public bool Contains(MyPoint point)
{
return (x < point.x && x + width >= point.x &&
y < point.y && y + height >= point.y);
}

public bool Contains(Rectangle other)
{
return Contains(new MyPoint((int)other.x,(int)other.y))
&& Contains(new MyPoint((int)(other.x+other.width),(int)(other.y+other.height)));
}

public override string ToString()
{
return "Rect:" + x + "," + y + "," + width;
}
}

### 过滤与直接预排序对比实现

#include<iostream>
#include<algorithm>
#include<vector>
#include <cstdlib>
#include <ctime>
using namespace std;
struct point{     //定义结构体
int x,y;
};
bool cmp(point a,point b){  //自定义排序方法
return a.y==b.y?a.x>b.x:a.y<b.y;  //y升序，x降序
}
int main(){
clock_t start,finish;
double totaltime;
std::srand(std::time(nullptr)); // use current time as seed for random generator
int count;
cout<<"输入点的个数和点：" ;
cin>>count;    cout<<"输入总点数为："<<count<<endl;
vector<point> p; //容器用来装平面上的点
for(int i=0;i<count;i++){
point temp;
temp.x = std::rand()% 100000000;
temp.y = std::rand()% 100000000;
p.push_back(temp); //为了方便对比性能，我们随机插入大量点
}    cout<<"------------------过滤后再使用预排序：------------------------------"<<endl;
start = clock();
vector<point> filter;//定义过滤容器
vector<point> res;  //定义结果容器
int curMaxRank = 0;
int curMaxIndex = 0;
for(int i=0;i<count;i++){
int temp =p[i].x+p[i].y-std::abs(p[i].x-p[i].y);
if(temp > curMaxRank)
{
curMaxRank = temp;
curMaxIndex = i;
}
}
for(int i=0;i<count;i++)
{
if(p[i].x >= p[curMaxIndex].x || p[i].y>= p[curMaxIndex].y)
{
filter.push_back(p[i]);
}
}

sort(filter.begin(),filter.end(),cmp);
res.push_back(filter[filter.size()-1]);  //左上角的那个点，一定符合条件
int maxx=filter[filter.size()-1].x;
for(int i=filter.size()-2;i>=0;i--){  //y从大到小，若i点x值大于所有比其y值大的点的x值，那么i点为“最大点”。
if(filter[i].x>maxx){
res.push_back(filter[i]);
maxx=filter[i].x;
}
}
finish = clock();
cout<<"过滤后点数量："<<filter.size()<<endl;
cout<<"符合条件的点数量："<<res.size()<<endl;
for(int i=0;i<res.size();i++){
printf("%d %d\n", res[i].x, res[i].y);
}
totaltime=(double)(finish-start)/CLOCKS_PER_SEC;
cout<<"\n此程序的运行时间为"<<totaltime<<"秒！"<<endl;
cout<<"------------------直接使用预排序：------------------------------"<<endl;
start = clock();
sort(p.begin(),p.end(),cmp);
res.clear();
res.push_back(p[p.size()-1]);  //左上角的那个点，一定符合条件
int maxX=p[p.size()-1].x;
for(int i=p.size()-2;i>=0;i--){
//y从大到小，若i点x值大于所有比其y值大的点的x值，那么i点为“最大点”。
if(p[i].x>maxX){
res.push_back(p[i]);
maxX=p[i].x;
}
}
finish = clock();
cout<<"符合条件的点数量："<<res.size()<<endl;
for(int i=0;i<res.size();i++){
printf("%d %d\n", res[i].x, res[i].y);
}
totaltime=(double)(finish-start)/CLOCKS_PER_SEC;
cout<<"\n此程序的运行时间为"<<totaltime<<"秒！"<<endl;

return 0;
} 
输入点的个数和点：......输入总点数为：500000
------------------过滤后再使用预排序：------------------------------

15480205 99999697
17427518 99999676
78059606 99999351
80881235 99998746
91608165 99997683
95825638 99996289
99690315 99993155
99874266 99991089
99884382 99978546
99908259 99961095
99942330 99858670
99963997 99157830
99975627 97385053
99996564 95654979
99998236 95378376
99999527 66461920

------------------直接使用预排序：------------------------------

15480205 99999697
17427518 99999676
78059606 99999351
80881235 99998746
91608165 99997683
95825638 99996289
99690315 99993155
99874266 99991089
99884382 99978546
99908259 99961095
99942330 99858670
99963997 99157830
99975627 97385053
99996564 95654979
99998236 95378376
99999527 66461920

### 下面的代码可以通过牛客网测试

#include<iostream>
#include<algorithm>
#include<vector>
#include <cstdlib>

using namespace std;
struct point{     //定义结构体
int x,y;
};
bool cmp(point a,point b){  //自定义排序方法
return a.y==b.y?a.x>b.x:a.y<b.y;  //y升序，x降序
}
point p[500001];
point filter[500001];
int main(){

int count;

scanf("%d",&count);

for(int i = 0; i < count; i++)
{
scanf("%d%d", &p[i].x, &p[i].y);
}

int curMaxRank = 0;
int curMaxIndex = 0;
for(int i=0;i<count;i++){
int temp =p[i].x+p[i].y-std::abs(p[i].x-p[i].y);
if(temp > curMaxRank)
{
curMaxRank = temp;
curMaxIndex = i;
}
}
int fCount =0 ;
for(int i=0;i<count;i++)
{
if(p[i].x >= p[curMaxIndex].x || p[i].y>= p[curMaxIndex].y)
{
filter[fCount++]=p[i];
}
}

sort(filter,filter+fCount,cmp);

int maxx=-1;
for(int i=fCount-1;i>=0;i--){  //y从大到小，若i点x值大于所有比其y值大的点的x值，那么i点为“最大点”。
if(filter[i].x>maxx){

printf("%d %d\n", filter[i].x, filter[i].y);
maxx=filter[i].x;
}
}

return 0;
}

### 总结与展望

posted on 2018-08-30 22:53  RonTang  阅读(6330)  评论(0编辑  收藏