GDPU 算法分析与设计 天码行空3 分治plus

一、【实验目的】

(1)熟悉分治算法的基本思想;
(2)理解分治算法改进的基本方法;
(3)理解不同情况下的算法时间复杂度的分析方法。

二、【实验内容】

平面上有n个点P1、P2、…Pi、…、Pn,n>1,Pi的坐标为(Xi,Yi)。请设计两种算法求距离最近的两个点及两者之间的距离,
并分析两种算法的时间复杂度。

要求算法测试时,点的个数不能少于10个。

提示:为保持与教材P34中算法参数的一致性,输入可采用3个数组,即:PN[]表示点的编号集合,PX[]表示对应X坐标,PY[]表示对应Y的坐标;
输出可以建立一个结构体,分别对应两个点的编号及对应的距离。以下是10个点的例子,其中点坐标都设为整数,可以用来做算法测试。
int PN[10]={0,1,2,3,4,5,6,7,8,9};

int PX[10]={-11,15,4,-5,2,3,-2,-7,-4,10};
int PY[10]={4,-3,6,-6,-1,2,7,5,-7,0};
另外,结构体可以采取如下定义:
typedef struct pS{
int iC1,iC2; //点的编号
float fD; //两点距离
} pT;

三、【实验源代码】

👨‍🏫 董晓老师题解

💖 ShortestDistance.java

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Scanner;

public class ShortestDistance
{
	static int n;// 点的数量
	static int[] px;// 横坐标
	static int[] py;// 纵坐标
	static Point[] ps;// 点集
	static Point[] pointPair = new Point[2];// 存最小点对的下标

//	点类
	static class Point
	{
		int id;
		int x;
		int y;

		@Override
		public String toString()
		{
			return "Point [id=" + id + ", x=" + x + ", y=" + y + "]";
		}

		public Point(int id, int x, int y)
		{
			super();
			this.id = id;
			this.x = x;
			this.y = y;
		}

	}

	public static void main(String[] args)
	{
//		输入测试
//		Scanner sc = new Scanner(System.in);
//		n = sc.nextInt();// 点的个数
//		px = new int[n];
//		py = new int[n];
//		for (int i = 0; i < n; i++)
//		{
//			px[i] = sc.nextInt();
//			py[i] = sc.nextInt();
//		}

		n = 10;// 点的个数
		ps = new Point[n];
		px = new int[] { -11, 15, 4, -5, 2, 3, -2, -7, -4, 10 };
		py = new int[] { 4, -3, 6, -6, -1, 2, 7, 5, -7, 0 };
		for (int i = 0; i < n; i++)
			ps[i] = new Point(i, px[i], py[i]);

		double distance1 = bruteForce();
		System.out.println("暴力法:" + pointPair[0] + " " + pointPair[1] + " " + distance1);
		Arrays.sort(ps, (p1, p2) -> {
			if (p1.x == p2.x)
				return p1.y - p2.y;
			return p1.x - p2.x;
		});
		double distance2 = partition(0, n - 1);
		System.out.println("分治法:" + pointPair[0] + " " + pointPair[1] + " " + distance2);
	}

//	方法2:分治(求闭区间 [l,r]的最小距离点对的距离,返回此距离)
	static double ans = Double.MAX_VALUE;// 存全局最小距离

	private static double partition(int l, int r)
	{
		if (l >= r)// 到头了
			return Double.MAX_VALUE;
		int mid = l + r >> 1;// m 是中点的下标
		ans = Math.min(ans, partition(l, mid));// 分治求左区间的点对最小距离
		ans = Math.min(ans, partition(mid + 1, r));// 分治求右区间的点对最小距离

		int midX = ps[mid].x;// 中线的位置
//		向下取整,因为最小距离点对分布在左右两区间的情况下,只有可能在开区间(midX-curMini, midX+curMini)
//		注意,因为坐标是整数,所以浮点数部分不可能取到(舍去)
		int lX = (int) (midX - ans);
		int rX = (int) (midX + ans);
		ArrayList<Point> midPoints = new ArrayList<>();// 存中间可能出现最小距离的最小点对
		for (int i = l; i <= r; i++)
			if (ps[i].x > lX && ps[i].x < rX)// 只添加有机会的点(减少计算量)
				midPoints.add(ps[i]);

		midPoints.sort((p1, p2) -> p1.y - p2.y);// 按纵坐标 y 排序
		int size = midPoints.size();
//		开始求跨越两区间的点对的最小距离
		for (int i = 0; i < size; i++)
		{
			Point a = midPoints.get(i);
			for (int j = i + 1; j < size; j++)
			{
				Point b = midPoints.get(j);
				// midPoints 按 y 排序,(a的y) 肯定<= (b的y)
				if (b.y - a.y > ans)// 如果当前 by-ay 已经 > curMini,那么后面的 by 只会更大,剪枝break
					break;
				double d = calDistance(a, b);
				if (d < ans)// 尝试更新答案
				{
					ans = d;
					ans = ans;
					pointPair[0] = a;
					pointPair[1] = b;
				}

			}
		}
		return ans;
	}

	// 方法1:暴力
	private static double bruteForce()
	{
		double res = Double.MAX_VALUE;
		for (int i = 0; i < n; i++)
			for (int j = i + 1; j < n; j++)
			{
//				测试代码
//				double t = calDistance(ps[i], ps[j]);
//				if (t < res)
//				{
//					System.out.print(ps[i] + " ");
//					System.out.print(ps[j] + " 距离:");
//					System.out.println(t);
//					res = Math.min(res, t);
//				}
				double d = calDistance(ps[i], ps[j]);
				if (d < res)
				{
					pointPair[0] = ps[i];
					pointPair[1] = ps[j];
					res = d;
				}

			}
		return res;
	}

//	返回点 p1 和 点p2 的欧几里得距离
	private static double calDistance(Point p1, Point p2)
	{
		return Math.sqrt(Math.pow(Math.abs(p1.x - p2.x), 2) + Math.pow(Math.abs(p1.y - p2.y), 2));
	}
}


四、【实验结果】

暴力法:Point [id=3, x=-5, y=-6] Point [id=8, x=-4, y=-7] 1.4142135623730951
分治法:Point [id=8, x=-4, y=-7] Point [id=3, x=-5, y=-6] 1.4142135623730951
  • 6
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值