分治算法--最近点问题java解法

问题描述:
最近对问题要求在包含有n个点的集合S中,找出距离最近的两个点。设 p1(x1,y1),p2(x2,y2),……,pn(xn,yn)是平面的n个点。
严格地将,最近点对可能不止一对,此例输出一对即可。

分治算法通过“分”,递归解决较小的问题;“治”从子问题的解构建原问题的解。在最近点问题中,我们通过不断将集合按照左右进行分割,找出局部最优解,然后从众多局部最优解找出最终最优解。
具体思路:
取横坐标的平均值X,将集合P分为左右两部PL,PR。最近两点可能都在左边,可能都在右边,也可能左右各一点。将这三种最近距离分别称为dl,dr,dc。
分治算法--最近点问题java解法_第1张图片
其中dl和dr可以递归的计算,注意递归的终点是集合的size等于1或者2,对这两种情况就要具体计算距离了。至于dc,我们令δ=min(dl,dr)。然后将横坐标与平均值X相距δ以內的点加入中间集合,若中间集合size达到2以上,计算出中间集合內两点的最短距离,与δ比较返回并最小值。
下面给出代码:
由于最终需要给出两点坐标,所以每次递归的返回值是包含了最近两点的数组。

import java.lang.Math;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;
//一个递归代码实现
//本代码实现寻找一组点列中的最近点问题,通过把点列不断分为左中右,通过递归实现查找

public class NearestNode {
    public static void main(String[] args) {
        System.out.println("请输入点的个数");
        Scanner in = new Scanner(System.in);
        double n = in.nextDouble();
        ArrayList<NearestNode> array = new ArrayList<NearestNode>();
        for (int i = 0; i <n ; i++) {
            System.out.println("请输入下一个点的x,y坐标");
            double x = in.nextDouble();
            double y = in.nextDouble();
            array.add(new NearestNode(x,y));
        }
        ArrayList<NearestNode> twonode = dis_compare(array);
        printarray(twonode);

    }

    //构造函数,使一个NearestNode对象具有x,y值
    public  NearestNode(double x, double y){
        this.x = x;
        this.y = y;
    }
    private double x;
    private double y;
    //递归函数,
    // @param array是包含平面点的点列。
    //return 返回包含两最近点的数组列表
    public static ArrayList<NearestNode> dis_compare(ArrayList<NearestNode> array){
        //找到平均横坐标
        double sumx=0;
        for (NearestNode node: array
             ) {
            sumx = sumx+node.x;
        }
        double midx = sumx/array.size();
        //通过midx将点集合分为左右两部分,计算出两部分中最小距离,用于构建中间一部分
        //两最近点可能都在左数组,可能都在右数组,也可能左右各一个,
        // 因此建立一个中间数组,将符合条件(x,y方向上不超过左右数组点最近距离)的点放入
        // 因为要返回数组列表,所以为左边部分和右边部分各准备一个目标数组装最近两点
        ArrayList<NearestNode> leftarray = new ArrayList<>();
        ArrayList<NearestNode> rightarray = new ArrayList<>();
        ArrayList<NearestNode> midarray = new ArrayList<>();
        ArrayList<NearestNode> targetlarray = new ArrayList<>();
        ArrayList<NearestNode> targetrarray = new ArrayList<>();
        ArrayList<NearestNode> targetarray = new ArrayList<>();

        double leftnearest;
        double rightnearest;
        for (NearestNode node: array
             ) {
            if (node.x<=midx) leftarray.add(node);
            else rightarray.add(node);
        }
        //数组大小分为三种情况
        //1.只有一个点   返回无穷距离,目标数组不变仍为空
        //2.有两个点     将两点装入目标左、右数组,计算出距离
        //3.多余两个点    将数组进一步递归分割
        if (leftarray.size()==1)
        {
            leftnearest=Double.POSITIVE_INFINITY;
        }
        else if (leftarray.size()==2) {

            leftnearest = dist(leftarray,0,1);
            targetlarray.add(leftarray.get(0));
            targetlarray.add(leftarray.get(1));}
        else {
            targetlarray = dis_compare(leftarray);
            leftnearest = dist(targetlarray,0,1);
            }
        if (rightarray.size()==1)
        {
            rightnearest=Double.POSITIVE_INFINITY;
        }
        else if (rightarray.size()==2) {
            rightnearest = dist(rightarray,0,1);
            targetrarray.add(rightarray.get(0));
            targetrarray.add(rightarray.get(1));
        }

        else
        {
            targetrarray = dis_compare(rightarray);
            rightnearest = dist(targetrarray,0,1);
        }


        //找出左右两目标数组中距离更近的一个,赋给目标数组
        //取左右数组最近点距离中更小值dis,用于中间数组的设置
        targetarray = leftnearest<rightnearest?targetlarray:targetrarray;
        double dis = dist(targetarray,0,1);
        //将距离midx一个nearest内的点归入midarray
        for (NearestNode node: array)
        {
            if (node.x>=midx-dis&&node.x<=midx+dis) {
                midarray.add(node);
            }
        }
        //查看中间数组是否存在,若存在则进行比较
        //将左右组中最小距离与中间数组最小距离进行比较,返回包含最近两点的数组
        ArrayList<NearestNode> res;
        if (midarray.size()>1)  {res = nearestdis(midarray,dis,targetarray);}
        else  res =targetarray;
        return res;

       
    }
    //@param array 中间数组
    //@param dis   左右数组最小值
    //@param targetarray 原左右部分中最优解
    //return 返回包含最近两点的数组
    public static ArrayList<NearestNode> nearestdis(ArrayList<NearestNode> array,double dis,ArrayList<NearestNode> targetarray) {
        int n = array.size();
        ArrayList<NearestNode> res = new ArrayList<>();
        double nearestdis = Double.POSITIVE_INFINITY;
        double tmp;
        int first=0;
        int second=0;
        for (int i = 0; i < n; i++) {
            for (int j = i + 1; j < n; j++) {
                if (array.get(i).y - array.get(j).y > dis) {} 
                else {
                    if (dist(array, i, j) < nearestdis) {
                        tmp = dist(array, i, j);
                        nearestdis = tmp < dis ? tmp : dis;
                        first=i;
                        second=j;
                    }
                }
            }
        }
        if (nearestdis == dis) {
            return targetarray;
        } else {
            res.add(array.get(first));
            res.add(array.get(second));
            return res;
        }

    }
    //@param array 包含点坐标的数组
    //@param i j 要进行比较的两点序列
    //return 返回两点距离
    //注意double型不能直接相减,要转化成BigDecimal
    public static double dist(ArrayList<NearestNode> array,int i, int j){
        BigDecimal xi = new BigDecimal(Double.toString(array.get(i).x));
        BigDecimal xj = new BigDecimal(Double.toString(array.get(j).x));
        BigDecimal yi = new BigDecimal(Double.toString(array.get(i).y));
        BigDecimal yj = new BigDecimal(Double.toString(array.get(j).y));
        double xdiff = xi.subtract(xj).doubleValue();
        double ydiff = yi.subtract(yj).doubleValue();

        return Math.sqrt(Math.pow(xdiff,2)+Math.pow(ydiff,2));
    }
    public static void printarray(ArrayList<NearestNode> array){
        //System.out.println("数组长度:"+array.size());
        for (NearestNode node: array
             ) {
            System.out.println("("+node.x+","+node.y+")");
        }
    }

}

你可能感兴趣的:(数据结构与算法)