首页 > 代码库 > 【算法设计与分析基础】15、最近对问题

【算法设计与分析基础】15、最近对问题

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;
	}
}

  

 

技术分享

 

 

 

最近点距离

技术分享

 

 

 

 

疑问,求解答,网上什么  “鸽巢原理”  不是很懂,求通俗点的解释。。。

 

技术分享

 

【算法设计与分析基础】15、最近对问题