1、由于Java中没有存放单个键值对的类型使用起来不是很方便
package cn.xf.util;
/**
*
* 功能:相当于一个key value
* @author xiaofeng
* @date 2017年6月18日
* @fileName GenericPair.java
*
*/
public class GenericPair<E extends Object, F extends Object> {
private E first;
private F second;
public GenericPair() {
}
public GenericPair(E first, F second) {
this.first = first;
this.second = second;
}
@Override
public String toString() {
String result = "["+ this.first.toString() + ", " + this.second.toString() +"]";
return result;
}
public E getFirst() {
return first;
}
public void setFirst(E first) {
this.first = first;
}
public F getSecond() {
return second;
}
public void setSecond(F second) {
this.second = second;
}
}
求最近键值对问题
package cn.xf.algorithm.ch05;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import cn.xf.util.GenericPair;
/**
*
* 功能:最近对问题
* @author xiaofeng
* @date 2017年6月18日
* @fileName MinPath.java
*
*/
public class MinPath {
/**
*
* @param points 数组points中存储了平面上的n >= 2个点,并且按照这些点的x轴坐标升序排序 seqXList seqYList
* @param qpoints 数据qpoint存储了与points相同的点,只是它是按照这点的Y轴坐标升序排序
* 输出:最近点对之间的欧几里得距离
*/
public double efficientClosestPair(List<GenericPair<Double, Double>> points, List<GenericPair<Double, Double>> qpoints) {
if(points.size() <= 3) {
//如果少于3个直接蛮力比较,求几点的最近距离
double countMin = Double.MAX_VALUE;
for(int i = 0; i < points.size(); ++i) {
for(int j = 0; j < points.size(); ++j) {
if(i == j) {
continue;
}
double temp = dist(points.get(i), points.get(j));
if(temp < countMin) {
countMin = temp;
}
}
}
return countMin;
} else {
int midIndex = points.size() / 2;
//吧points前面一半提取出来pointsl
List<GenericPair<Double, Double>> pointsl = new ArrayList<GenericPair<Double,Double>>();
for(int i = 0; i < midIndex; ++i) {
pointsl.add(points.get(i));
}
//把qpoints前面一半提取出来qpointsl
// List<GenericPair<Double, Double>> qpointsl = new ArrayList<GenericPair<Double,Double>>();
// for(int i = 0; i < midIndex; ++i) {
// qpointsl.add(qpoints.get(i));
// }
//pointsl 的Y排序版本作为新的qpointsl
List<GenericPair<Double, Double>> qpointsl = new ArrayList<GenericPair<Double,Double>>();
for(int i = 0; i < midIndex; ++i) {
qpointsl.add(points.get(i));
}
seqYList(qpointsl, 0, qpointsl.size() - 1);
//把points后面一半提取出来pointsr
List<GenericPair<Double, Double>> pointsr = new ArrayList<GenericPair<Double,Double>>();
for(int i = midIndex; i < points.size(); ++i) {
pointsr.add(points.get(i));
}
//吧qpoints后面一半提取出来qpointsr
// List<GenericPair<Double, Double>> qpointsr = new ArrayList<GenericPair<Double,Double>>();
// for(int i = midIndex; i < qpoints.size(); ++i) {
// qpointsr.add(qpoints.get(i));
// }
List<GenericPair<Double, Double>> qpointsr = new ArrayList<GenericPair<Double,Double>>();
for(int i = midIndex; i < points.size(); ++i) {
qpointsr.add(points.get(i));
}
seqYList(qpointsr, 0, qpointsr.size() - 1);
//用新的集合点进入递归
double dl = efficientClosestPair(pointsl, qpointsl);
double dr = efficientClosestPair(pointsr, qpointsr);
//两个距离取小的
double dmin = minValue(dl, dr);
double dminDist = Math.pow(dmin, 2);
//取X值得中间分段
double midPointX = points.get(midIndex).getFirst();
//吧所有这些点中X-midPointX的距离比dmin小的提取出来
List<GenericPair<Double, Double>> pointsDmin = new ArrayList<GenericPair<Double,Double>>();
for(int i = 0; i < qpoints.size(); ++i) {
//绝对值比最小的距离还小
if(Math.abs(qpoints.get(i).getFirst() - midPointX) < dmin) {
//比两边最小距离,横向更小的值,这个是按照Y升序的数据
pointsDmin.add(qpoints.get(i));
}
}
//遍历这个集合中的所有数据,获取最小距离
for(int i = 0; i < pointsDmin.size(); ++i) {
//如果K超出范围,就不用算了
int k = i + 1;
if(k >= pointsDmin.size()) {
break;
}
//求平方差
double minY2 = Math.pow(pointsDmin.get(k).getSecond() - pointsDmin.get(i).getSecond(), 2);
while(k < pointsDmin.size() && minY2 < dminDist) {
//在数据范围内,然后Y的平方差比最小的还小,那么就可以进行比较了
double tempMin = Math.pow(pointsDmin.get(k).getFirst() - pointsDmin.get(i).getFirst(), 2) + Math.pow(pointsDmin.get(k).getSecond() - pointsDmin.get(i).getSecond(), 2);
dminDist = minValue(tempMin, dminDist);
++k;
}
}
return Math.sqrt(dminDist);
}
}
public double minValue(double dl, double dr) {
if(dl < dr) {
return dl;
} else {
return dr;
}
}
public static double dist(GenericPair<Double, Double> point1, GenericPair<Double, Double> point2) {
//求出两点之间的距离
//求平方差
double distx = Math.abs(point1.getFirst() - point2.getFirst());
double disty = Math.abs(point1.getSecond() - point2.getSecond());
//求平方差的和的平方根
return Math.sqrt(distx * distx + disty * disty);
}
@Test
public void quikSortTest() {
List<GenericPair<Double, Double>> points = new ArrayList<GenericPair<Double,Double>>();
List<GenericPair<Double, Double>> qpoints = new ArrayList<GenericPair<Double,Double>>();
//5,3,1,9,8,2,4,7,8
//4,1,8,2,11,9,7,15,11
GenericPair<Double, Double> el1 = new GenericPair<Double, Double>(5d, 4d);
GenericPair<Double, Double> el2 = new GenericPair<Double, Double>(3d, 1d);
GenericPair<Double, Double> el3 = new GenericPair<Double, Double>(1d, 8d);
GenericPair<Double, Double> el4 = new GenericPair<Double, Double>(9d, 2d);
GenericPair<Double, Double> el5 = new GenericPair<Double, Double>(8d, 11d);
GenericPair<Double, Double> el6 = new GenericPair<Double, Double>(2d, 9d);
GenericPair<Double, Double> el7 = new GenericPair<Double, Double>(4d, 7d);
GenericPair<Double, Double> el8 = new GenericPair<Double, Double>(7d, 15d);
// GenericPair<Double, Double> el9 = new GenericPair<Double, Double>(8d, 11d);
points.add(el1);points.add(el2);points.add(el3);points.add(el4);points.add(el5);
points.add(el6);points.add(el7);points.add(el8);//points.add(el9);
qpoints.add(el1);qpoints.add(el2);qpoints.add(el3);qpoints.add(el4);qpoints.add(el5);
qpoints.add(el6);qpoints.add(el7);qpoints.add(el8);//qpoints.add(el9);
MinPath mp = new MinPath();
mp.seqXList(points, 0, points.size() - 1);
mp.seqYList(qpoints, 0, qpoints.size() - 1);
// for(GenericPair<Double, Double> temp : points){
// System.out.print(temp.toString() + "\t");
// }
double result = mp.efficientClosestPair(points, qpoints);
System.out.println(result);
}
/**
* 按照X升序
* @param points
* @return
*/
public static void seqXList(List<GenericPair<Double, Double>> points, int start, int end) {
//获取中间点位置,然后一分为二,进行遍历递归
if(start < end) {
int mid = hoarePartition(points, false, start, end);
seqXList(points, start, mid - 1);
seqXList(points, mid + 1, end);
}
}
/**
* 按照Y升序
* @param points
* @return
*/
public static void seqYList(List<GenericPair<Double, Double>> points, int start, int end) {
// 获取中间点位置,然后一分为二,进行遍历递归
if (start < end) {
int mid = hoarePartition(points, true, start, end);
seqYList(points, start, mid - 1);
seqYList(points, mid + 1, end);
}
}
/**
* 寻找分裂点,并规整数据,xOry是用来判定两边是用x-false排序还是用y-true排序
* @param points
* @return
*/
public static int hoarePartition(List<GenericPair<Double, Double>> points, boolean xOry, int start, int end) {
if(start >= end) {
return start;
}
//以第一个数为准对进行分裂
double temp = getValue(points.get(start), xOry);
//两边进行遍历,直到两个错开,或者相等
int leftIndex = start + 1;
int rightIndex = end;
while(leftIndex < rightIndex) {
//如果遍历到左边的比第一个大,右边比第一个数小,交换数据,为了避免多余的一次交换
while(getValue(points.get(leftIndex), xOry) < temp) {
++leftIndex;
}
while(getValue(points.get(rightIndex), xOry) > temp) {
--rightIndex;
}
//如果是到了最后一步,错位了,然后交换了,避免多余的一次交换
swap(points, leftIndex, rightIndex);
}
//从新吧最后一对错位的数据交换回来,但是如果这个循环根本没有进去过,那么就不应该有这个交换
swap(points, leftIndex, rightIndex);
//最后把j对应的数据地方交换到开始作为中间点的数据位置
//如果本身比要排列的中间值还小,那么不用换,换了就打乱了排序
if(getValue(points.get(start), xOry) > getValue(points.get(rightIndex), xOry))
swap(points, start, rightIndex);
return rightIndex;
}
//交换数据
public static void swap(List<GenericPair<Double, Double>> points, int i, int j) {
//取出i位置的数据,吧i数据修改为j,temp为i的数据,吧j替换为temp
GenericPair tempi = points.get(i);
GenericPair tempj = points.get(j);
points.set(i, tempj);
points.set(j, tempi);
}
/**
* xOry是用来判定两边是用x-false排序还是用y-true排序
* @param pair
* @param xOry
* @return
*/
public static double getValue(GenericPair<Double, Double> pair, boolean xOry) {
double temp = 0;
if(!xOry) {
//如果是true,那么就是判断y
//否则是第一个x
temp = pair.getFirst();
} else {
temp = pair.getSecond();
}
return temp;
}
}
最近点距离
疑问,求解答,网上什么 “鸽巢原理” 不是很懂,求通俗点的解释。。。