假设有一组坐标集[[1,2],[11,22],[21,3],[34,23],[45,6],[22,54],[31,20],[13,46],[11,8],[4,21],[19,26],[21,42]...]
1、初步方法
对于这个问题,最简单的方法就是通过for循环,但是重复计算导致计算效率过低。
2、改进方法,利用几何性质,最远点必然存在于坐标组成的图形的边界上,即图形的某个顶点对上。
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Scanner;
public class Maxinarray {
/**
* <p>Title: Maxinarray</p>
* <p>Description: </p>
* @author 李岩(liyanrufeng@yeah.net)
* @date 2014-9-11
*/
static int arraycount=500;//总共有arraycount组坐标
static int xlimit=1000; //坐标x的取值范围
static int ylimit=1000; //坐标y的取值范围
boolean[] flag;//flag[i]标记A[i]是否已在顶点中
Queue< Integer> Q=new LinkedList< Integer>();//点集A的顶点,
int n;
int l;
public void sort1(ArrayList<int[]> list){
/**
* <p>Title: sort1</p>
* <p>Description:枚举方法1 时间复杂度n^2 </p>
*/
long startTime = System.currentTimeMillis();
int d0=0;
int [] max = new int[2];
max[0] = 0;
max[1] = 0;
for(int i = 0;i<list.size();i++)
{
for(int j = 0;j<list.size();j++)
{
int d=distance(list.get(i),list.get(j));
if(d > d0)
{
max[0]=i;
max[1]=j;
d0=d;
}
}
}
System.out.println(list.get(max[0])[0]+","+list.get(max[0])[1]+"---"+list.get(max[1])[0]+","+list.get(max[1])[1]);
System.out.println(Math.sqrt(d0));
System.out.println(list.size()+"组坐标,方法1耗时"+(System.currentTimeMillis()-startTime)+"ms");
}
public void sort2(ArrayList<int[]> list){
/**
* <p>Title: sort2</p>
* <p>Description:枚举方法1改进,将j的起点从i+1开始, 时间复杂度n^2/2 </p>
*/
long startTime = System.currentTimeMillis();
int d0=0;
int [] max = new int[2];
max[0] = 0;
max[1] = 0;
for(int i = 0;i<list.size();i++)
{
for(int j = i+1;j<list.size();j++)
{
int d=distance(list.get(i),list.get(j));
if(d > d0)
{
max[0]=i;
max[1]=j;
d0=d;
}
}
}
System.out.println(list.get(max[0])[0]+","+list.get(max[0])[1]+"---"+list.get(max[1])[0]+","+list.get(max[1])[1]);
System.out.println(Math.sqrt(d0));
System.out.println(list.size()+"组坐标,方法2耗时"+(System.currentTimeMillis()-startTime)+"ms");
}
public void sort3(ArrayList<int[]> list) {
/**
* <p>Title: sort3</p>
* <p>Description:利用几何性质,最远点必然存在于坐标组成的图形的边界上,即图形的某个顶点对上 </p>
* 寻找顶点的方法是先把坐标数据按照x值排序,x相等则y值小者在前,重复点只保留一个然后将所有坐标都映射在xy坐标轴上。
* 这样以xy轴最左边的点为基准点,然后遍历所有点集,并点坐标按其同基准点的方向k排序,k若想等则保留x最大点,存入在数组p中。
* 然后再从基准点开始,顺时针依次遍历每一个坐标,当遍历至p[n+1]时,比较m(p[n+1],p[n])和n(p[n],p[n-1])的方向,
* 如果m的方向在n的顺时针转动方向,则将p[n+1]设为顶点,否则将p[n+1]设为顶点,并把p[n]从顶点集中移除。
*
*/
long startTime = System.currentTimeMillis();
int d0=0;
int [] max = new int[2];
max[0] = 0;
max[1] = 0;
ArrayList<int[]> lists=getpoints(list);
sort2(lists);
//System.out.println(list.get(max[0])[0]+","+list.get(max[0])[1]+"---"+list.get(max[1])[0]+","+list.get(max[1])[1]);
//System.out.println(Math.sqrt(d0));
System.out.println(list.size()+"组坐标,方法3耗时"+(System.currentTimeMillis()-startTime)+"ms");
}
public int distance(int[] xy1, int[] xy2) {
/**
* <p>Title: distance</p>
* <p>Description: 计算平方值 </p>
*/
int res = (int) (Math.pow((xy1[0]-xy2[0]), 2)+Math.pow((xy1[1]-xy2[1]), 2));
return res;
}
public boolean same(int[] xy1, int[] xy2) {
/**
* <p>Title: same</p>
* <p>Description: 判断两点是否相等 </p>
*/
return (xy1[0] == xy2[0] && xy1[0]== xy2[0]);
}
// 比较两个向量p1和p2的方向
public int cross(int[] xy1, int[] xy2, int[] base) {
/**
* <p>Title: cross</p>
* <p>Description: 返回两个向量的叉积</p>
* 设矢量 P = (x1, y1), Q = (x2, y2),则 P * Q = x1 * y2 - x2 * y1;
* 其结果是一个由 (0, 0), P, Q, P + Q 所组成的平行四边形的 带符号的面积
叉积的一个非常重要的性质是可以通过它的符号来判断两矢量相互之间的顺逆时针关系:
若 P * Q > 0,则 P 在 Q 的顺时针方向
若 P * Q < 0, 则 P 在 Q 的逆时针方向
若 P * Q = 0,则 P 与 Q 共线,但不确定 P, Q 的方向是否相同
*/
return (xy1[0] - base[0])*(xy2[1]-base[1])-(xy2[0]-base[0])*(xy1[1]-base[1]);
}
//计算顶点
@SuppressWarnings("unchecked")
public ArrayList<int[]> getpoints(ArrayList<int[]> list){
ArrayList<int[]> points = new ArrayList<int[]>();//存放非重复坐标
ArrayList<int[]> res = new ArrayList<int[]>();//存放凸点
Collections.sort(list, new sorts());
int[] base=list.get(0); //将第1个点预设为最小点
points.add(base);
for (int[] point:list) {
//如果当前点的x值小于最小点,或x值相等,y值较小
if (points.contains(point)) //除去重复点
{;}
else points.add(point);
}
boolean[] flag = new boolean[points.size()];
n=points.size();
//points[0]必然为顶点
flag[1]=true;//将points[1]标记为顶点
res.add(points.get(1));
int last=1;
while(true){
int min=-1;
for(int i=1;i<n;i++)
if(!flag[i])
{//找一个不在顶点上的点
min=i;
break;
}
if(min==-1) break;//找不到,结束
for(int i=1;i<n;i++) //遍历所有点, 每个点都和现有最外侧的点比较,得到新的最外侧的点
if((cross(points.get(last),points.get(i),points.get(min))>0)|| (cross(points.get(last),points.get(i),points.get(min)) == 0) &&
(distance(points.get(last), points.get(i)) > distance(points.get(last), points.get(min))))
min=i;
if(flag[min]) break;//找不到最外侧的点了
res.add(points.get(min));//最外侧的点进顶点
flag[min]=true;//标记这点已放进顶点了
last=min;
}
last=1;
res.add(points.get(0));
// for(int[]point:res)
// {
// System.out.println(point[0]+","+point[1]+"---");
//
// }
return res;
}
public static void main(String args[]) {
ArrayList<int[]> list = new ArrayList<int[]>();
for(int i = 0;i<arraycount;i++)
{ int[] points = new int[2];
points[0] = (int) (Math.random()*xlimit-xlimit/2);
points[1] = (int) (Math.random()*ylimit-ylimit/2);
list.add(points);
//System.out.println(points[0]+" , "+points[1]);
}
Maxinarray sort = new Maxinarray();
//sort.sort1(list);
sort.sort2(list);
sort.sort3(list);
//Maxinarray[] list1 = new Maxinarray[10];
//List<Maxinarray> list2=new ArrayList<Maxinarray>();
//list2.add(sort);
}
}
class sorts implements Comparator {
@Override
public int compare(Object arg0, Object arg1) {
/**
* <p>Title: compare</p>
* <p>Description:重写排序函数,强制对对象进行排序</p>
*/
int[] xy1 = (int[])arg0;
int[] xy2 = (int[])arg1;
if( xy1[1]>xy2[1] )
return 1;
else if( xy1[1]==xy2[1] )
if(xy1[0]>xy2[0])
return 1;
else if(xy1[0]==xy2[0])
return 0;
return -1;
}
}