最近点对问题(算法与数据结构设计)

课题内容和要求

最近点对问题,在二维平面上输入n个点列P。其中任一点pi=(xi,yi),编写程序求出最近的两个点。使用穷举法实现,算法复杂度O(n2);优化算法,以O(nlog2n)实现这一问题

数据结构说明

Point_X结构体,用于表示点集,n用来存放点集中点的数量,dis_min用来存放求得的最近点对的距离,p数组用于存放所有点的x轴和y轴坐标。

Point结构体,用于存放点的x轴和y轴坐标。

Point_Y结构体,辅助数据结构,用于求分治法中计算跨越左子部分和右子部分存在的解,其中flag用于标记属于左子部分或右子部分,0表示左子部分,1表示右子部分。

算法设计

穷举法求最近点对,利用双重循环嵌套求每个点与其余点的距离,每当有最小的距离出现,记录两个点以及它们的最小距离。

分治法求最近点对,在主程序中对点集X按x轴由小到大排序,以及对点集X的复制点集Y按y轴由小到大排序,在递归函数中不断递归求中点X[mid]并分划为两部分,直至点被分划到少于或等于3个点,直接求它们中的最近点对以及最短距离min。递的部分完成后开始归,从左右子部分当中取最小的min,并在点集Y中直接取属于(X[mid]-min,X[mid]+min)的点存放到点集Y_中,并把属于(X[mid]-min,X[mid])的左子部分做标记flag=0,把属于(X[mid+1],X[mid]+min)的右子部分做标记flag=1。接着在点集Y_中找属于左子部分的点,每次找到一个属于左子部分的点,在此基础向上找(因为按y轴排序)四个属于右子部分的点,向下找四个属于右子部分的点,一共8个点,计算左子部分找到点与其对应向上向下的8个点的距离求最短距离。

详细设计

程序包含文件main.c、point.h、point.c

main.c

#include <stdlib.h>
#include <stdio.h>
#include "point.h"
#include <math.h>
int main()
{Point_X *X = (Point_X*)malloc(sizeof(Point_X));#if 1   // 免输入直接出结果,已给出点集合double p[10][2] = {{0,5},{-2,2},{0,1},{2,1},{-5,0},{-0.5,0},{0,0},{0.5,0},{5,0},{0,-5}};if(!Init(X,10)){printf("点集初始化失败\n");return 0;}Point *Y = (Point*)malloc((X->n)*sizeof(Point));if(!Y){printf("申请空间失败——辅助数据Y\n");}for(int i = 0;i<(X->n);i++){X->p[i].x = p[i][0];X->p[i].y = p[i][1];Y[i].x = p[i][0];Y[i].y = p[i][1];}#elif 0   // 需要用户输入int n = 0;  // 存放点集的数量,用来初始化点集printf("请输入点集中点的数量:\n");scanf("%d",&n);if(!Init(X,n)){printf("点集初始化失败\n");return 0;}Point *Y = (Point*)malloc((X->n)*sizeof(Point));if(!Y){printf("申请空间失败——辅助数据Y\n");}for(int i = 0;i<(X->n);i++){printf("请输入第%d个点的x轴:",i+1);scanf("%lf",&(X->p[i].x));printf("请输入第%d个点的y轴:",i+1);scanf("%lf",&(X->p[i].y));Y[i].x = X->p[i].x;Y[i].y = X->p[i].y;}getchar();  // 将上一个输入的回车接收#endif// 点对1,用于记录穷举法得出的最近点对Point pair_pp1;Point pair_pp2;// 点对2,用于记录分治法求得出得最近点对Point pair_p1;Point pair_p2;double min = INFINITY;for(int i = 0;i<X->n-1;i++)    // 穷举法求最近点对{for(int j=i+1 ;j<X->n;j++){double dis = distance(X->p[i],X->p[j]);if(dis<min) // 当dis小于min值更新最近点对的点以及两点之间的距离{pair_pp1 = X->p[i];pair_pp2 = X->p[j];min = dis;}}}printf("以下为穷举法求得的最近点对:\n");printf("点集中最近的两个点,点1:(%.2lf,%.2lf),点2:(%.2lf,%.2lf),两点距离:%.2lf\n",pair_pp1.x,pair_pp1.y,pair_pp2.x,pair_pp2.y,min);printf("---------------------------\n");QuickSort_X(X->p,0,X->n-1); // 调用针对x轴排序的快速排序算法对点集X排序QuickSort_Y(Y,0,X->n-1);    // 调用针对y轴排序的快速排序算法对点集Y排序close_point(X,Y,0,X->n-1,&pair_p1,&pair_p2);    // 调用分治法求最近点对printf("以下为分治法求得的最近点对:\n");printf("点集中最近的两个点,点1:(%.2lf,%.2lf),点2:(%.2lf,%.2lf),两点距离:%.2lf\n",pair_p1.x,pair_p1.y,pair_p2.x,pair_p2.y,X->dis_min);printf("输入回车结束程序。\n");    getchar();  // 输入回车结束程序free(Y);free(X->p);free(X);
}

point.h

#ifndef __POINT_H__
#define __POINT_H__
typedef struct point
{double x;double y;
}Point;
typedef struct point_x
{Point *p;   // 指向点数组Point的头指针double dis_min; // 记录点集中两点最小的距离int n;  // 点的数量
}Point_X;
typedef struct point_y  // 辅助数据结构
{Point p;int flag;   // 表示位,0表示属于左半边,1表示属于右半边
}Point_Y;
int Init(Point_X *X, int n);    // 初始化点集
void Swap(Point *p,int i, int j);   // 交换函数
int Partition_X(Point *p,int left,int right);   // 分化函数,针对x轴
void QuickSort_X(Point *p, int left, int right);    // 快速排序,针对x轴进行排序
int Partition_Y(Point *p,int left,int right);   // 分划函数,针对y轴分划
void QuickSort_Y(Point *p, int left, int right);    // 快速排序,针对y轴排序
double distance(Point p1, Point p2);    // 求点的距离
void close_point(Point_X *X, Point *Y, int left, int right, Point *pair_p1,Point *pair_p2); // 分治法求最近点对
#endif

point.c

#include "point.h"
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
/*** @brief 初始化点集* @param X 指向点集的指针* @param n 点的数量* @return 0,失败;1,成功
*/
int Init(Point_X *X, int n)
{X->p = (Point*)malloc(n*sizeof(Point));if(!X->p){return 0;}X->dis_min = INFINITY;X->n = n;return 1;
}
/*** @brief 交换函数* @param p 点数组* @param i 数组下标i* @param j 数组下标j* @return void
*/
void Swap(Point *p,int i, int j)
{Point tmp;if(i==j){return;}tmp = p[i];p[i] = p[j];p[j] = tmp;
}
/*** @brief 分化函数,针对x轴* @param p 点数组* @param left 左边界* @param right 右边界* @return j,分划元素下标
*/
int Partition_X(Point *p,int left,int right)
{   int i = left;int j = right+1;do{do{i++;}while(p[i].x<p[left].x&&i<=right);do{j--;} while(p[j].x>p[left].x);if(i<j){Swap(p,i,j);}             }while(i<j);Swap(p, left, j);return j;
}
/*** @brief 快速排序,针对x轴进行排序* @param p 点数组* @param left 左边界* @param right 右边界* @return void
*/
void QuickSort_X(Point *p, int left, int right)
{if(left<right){int j = Partition_X(p, left, right);QuickSort_X(p, left, j-1);QuickSort_X(p, j+1,right);}
}
/*** @brief 分划函数,针对y轴分划* @param p 点数组* @param left 左边界* @param right 右边界* @return j,分划元素下标
*/
int Partition_Y(Point *p,int left,int right)
{   int i = left;int j = right+1;do{do{i++;}while(p[i].y<p[left].y&&i<=right);do{j--;} while(p[j].y>p[left].y);if(i<j){Swap(p,i,j);}             }while(i<j);Swap(p,left,j);return j;
}
/*** @brief 快速排序,针对y轴排序* @param p 点数组* @param left 左边界* @param right 右边界* @return void
*/
void QuickSort_Y(Point *p, int left, int right)
{if(left<right){int j = Partition_Y(p, left, right);QuickSort_Y(p, left, j-1);QuickSort_Y(p, j+1,right);}
}
/*** @brief 求两点的距离* @param p1 点1* @param p2 点2* @return 返回两点的距离
*/
double distance(Point p1, Point p2)
{return sqrt(pow((p1.x-p2.x),2)+pow((p1.y-p2.y),2));
}
/*** @brief 分治法求最近点对* @param X 对于x轴已经排序好的点集* @param Y 对于y轴已经排序好的点集* @param left X点集左边界* @param right X点集右边界* @param pair_1 最近点对的点1* @param pair_2 最近点对的点2* @return void
*/
void close_point(Point_X *X, Point *Y, int left, int right, Point *pair_p1,Point *pair_p2)
{if(right-left<=2)   // 少于或等于三个点直接计算{if(left==right) // 只有一个点,直接返回{return;}for(int i = left;i<right;i++){for(int j = i+1;j<right+1;j++){double dis = distance(X->p[i],X->p[j]);if(X->dis_min>dis){X->dis_min = dis;pair_p1->x = X->p[i].x;pair_p1->y = X->p[i].y;pair_p2->x = X->p[j].x;pair_p2->y = X->p[j].y;}}}return;}int mid = (right+left)/2;   // 分划点close_point(X,Y,left,mid,pair_p1,pair_p2);  // 对左边集合进行计算close_point(X,Y,mid+1,right,pair_p1,pair_p2);   // 对右边进行计算int num_point = 1;  // 用于计算在范围(X[mid]-min,X[mid]+min)内的点的数量int mid_ = mid-1;while(X->p[mid_].x>(X->p[mid].x-X->dis_min)&&mid_>=0) // 计算在(X[mid]-min,X[mid])范围的点{num_point++;mid_--;}mid_ = mid+1;while(X->p[mid_].x<X->p[mid].x+X->dis_min&&mid_<=X->n)  // 计算在(X[mid],X[mid]+min)范围的点{num_point++;mid_++;}// 在Y点集中提取属于(X[mid]-min,X[mid]+mid)的点,并创建Y_集合存放Point_Y *Y_ = (Point_Y*)malloc(num_point*sizeof(Point_Y));if(!Y_){printf("申请空间失败——Y_\n");}int j = 0;  // Y_点集下标for(int i = 0;i<X->n;i++){if(Y[i].x>((X->p[mid].x)-(X->dis_min))&&Y[i].x<=X->p[mid].x)  // 属于(X[mid]-min,X[mid])存入Y_集合并做标记{Y_[j].p = Y[i];Y_[j].flag = 0;j++;}if(Y[i].x>X->p[mid].x&&Y[i].x<X->p[mid].x+X->dis_min)   // 属于(X[mid],X[mid]+min)存入Y_集合并做标记{Y_[j].p = Y[i];Y_[j].flag = 1;j++;}}for(int i = 0;i<num_point;i++) // 计算(X[mid]-min,X[mid]+min)范围内的最小距离{if(Y_[i].flag==0)   // 取点集合Y_属于(X[mid]-min,X[mid])的点,并计算每个点与属于(X[mid],X[mid]+min)的最多8个点进行计算{int four = 0;   // 用于计数for(int j = i-1;j>=0;j--)   // 向前找最多4个点进行计算{if(Y_[j].flag==0){continue;}four++;double dis = distance(Y_[i].p,Y_[j].p);if(X->dis_min>dis){X->dis_min = dis;pair_p1->x = X->p[i].x;pair_p1->y = X->p[i].y;pair_p2->x = X->p[j].x;pair_p2->y = X->p[j].y;}if(four==4) // 最多找4个点,退出该循环{four=0;break;}}four=0;for(int j = i+1;j<num_point;j++)    // 向后找最多4个点进行计算{if(Y_[j].flag==0){continue;}four++;double dis = distance(Y_[i].p,Y_[j].p);if(X->dis_min>dis){X->dis_min = dis;pair_p1->x = X->p[i].x;pair_p1->y = X->p[i].y;pair_p2->x = X->p[j].x;pair_p2->y = X->p[j].y;}if(four==4) // 最多找4个点,退出该循环{four = 0;break;}}
four=0;}}free(Y_);return;
}

测试数据及其结果分析

例子1,程序中自带的例子:
(0,5)(-2,2)(0,1)(2,1)(-5,0)(-0.5,0)(0,0)(0.5,0)(5,0)(0,-5)

运行结果:
在这里插入图片描述

例子2,手动输入(程序中默认启用例子1,需要在main.c文件中将条件编译中if后的1改为0,elif后的0改为1):
(0,0)(1,1)(0.25,0)(1.1,1)(-2,1)(2,2)(3,3)(-1,-1)

运行结果:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/868660.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

静脉分割YOLOV8-SEG

静脉分割&#xff0c;YOLOV8*SEG资源-CSDN文库 首先使用YOLOV8-SEG训练&#xff0c;得到PT模型&#xff0c;然后转换成ONNX&#xff0c;OPENCV的DNN调用&#xff0c;从而摆脱PYTORCH依赖&#xff0c;支持C,PYTHON,ANDROID调用

Java信号量semaphore的原理与使用方法

Semaphore的基本概念 在Java中&#xff0c;Semaphore是位于java.util.concurrent包下的一个类。它的核心就是维护了一个许可集。简单来说&#xff0c;就是有一定数量的许可&#xff0c;线程需要先获取到许可&#xff0c;才能执行&#xff0c;执行完毕后再释放许可。 那么&…

尚品汇-(十五)

&#xff08;1&#xff09;快速入门 SpringBoot形式创建 Maven形式创建&#xff1a; 加入依赖&#xff1a; 创建启动类&#xff1a; 设置头文件 就想Jsp的<%Page %>一样 &#xff0c;Thymeleaf的也要引入标签规范。不加这个虽然不影响程序运行&#xff0c;但是你的idea…

顶会FAST24最佳论文|阿里云块存储架构演进的得与失-4.EBS不同架构性能提升思路

3.1 平均延迟与长尾延迟 虚拟磁盘&#xff08;VD&#xff09;的延迟是由其底层架构决定的&#xff0c;具体而言&#xff0c;取决于请求所经历的路径。以EBS2为例&#xff0c;VD的延迟受制于两跳网络&#xff08;从BlockClient到BlockServer&#xff0c;再至ChunkServer&#x…

Xilinx FPGA:vivado关于IIC的一些零碎知识点

一、简介 IlC(inter-Integrated circuit)总线是一种由NXP(原PHILIPS)公司开发的两线式串行总线&#xff0c;用于连接微控制器及其外围设备。多用于主控制器和从器件间的主从通信&#xff0c;在小数据量场合使用&#xff0c;传输距离短&#xff0c;任意时刻只能有一个主机等特性…

13 协程设计原理与汇编实现

协程的问题 为什么要有协程?协程的原语操作?协程的切换?协程的struct如何定义?协程的scheduler(调度)如何定义?调度策略如何实现?协程如何与posix,api兼容?协程多核模式?协程的性能如何测试?为什么要有协程 同步的编程方式,异步的性能。同步编程时,我们需要等待io就…

信息技术课堂纪律管理:从混乱到秩序的智慧转型

引言&#xff1a; 在信息爆炸的时代&#xff0c;信息技术课程如同一把开启未来世界大门的钥匙&#xff0c;为学生们搭建起探索科技奥秘的桥梁。然而&#xff0c;面对着屏幕背后的无限诱惑&#xff0c;维持课堂纪律&#xff0c;确保学生们专注于学习&#xff0c;成为了每位信息…

C/C++内存分布

1.内存分布简略图 2.全局变量和静态变量的区别 (1)局部静态变量&#xff1a;存储在数据段中&#xff0c;局部静态变量的作用域在当前函数中&#xff0c;出了函数就不能使用该变量&#xff0c;但局部静态变量的生命周期是在整个程序间&#xff0c;局部静态变量要运行到这一行才…

【Java14】构造器

Java中的构造器在创建对象&#xff08;实例&#xff09;的时候执行初始化。Java类必须包含一个或一个以上的构造器。 Java中的构造器类似C中的构造函数。 Java中对象&#xff08;object&#xff09;的默认初始化规则是&#xff1a; 数值型变量初始化为0&#xff1b;布尔型变量…

【CSAPP】-cachelab实验

目录 实验目的与要求 实验设备与软件环境 实验过程与结果&#xff08;可贴图&#xff09; 操作异常问题与解决方案 实验总结 实验目的与要求 1、掌握应用程序性能的优化方法&#xff1b; 2、理解存储器层次结构在程序运行过程中所起的重要作用&#xff1b; 3、让学生更好…

高考志愿填报的六个不要

在高考志愿填报这个关键时刻&#xff0c;确实需要谨慎行事&#xff0c;避免一些常见的错误。以下是高考志愿填报的六个“不要”&#xff0c;希望能为你提供一些有用的建议&#xff1a; 1、不要盲目跟风 每个人的兴趣、能力和未来规划都不同&#xff0c;不要仅仅因为某个专业或…

Gradle基础:从入门到掌握

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 在现代软件开发中&#xff0c;自动化构建工具是提高效率和管理依赖的重要手段。而Gradle作为一种灵活且强大的构…

python基础篇(9):模块

1 模块简介 Python 模块(Module)&#xff0c;是一个 Python 文件&#xff0c;以 .py 结尾. 模块能定义函数&#xff0c;类和变量&#xff0c;模块里也能包含可执行的代码. 模块的作用: python中有很多各种不同的模块, 每一个模块都可以帮助我们快速的实现一些功能, 比如实现…

工业4.0视角下:PLC转OPC UA网关的作用

在工业自动化领域&#xff0c;PLC&#xff08;可编程逻辑控制器&#xff09;是常见的控制设备&#xff0c;而OPC UA&#xff08;开放型工业自动化统一架构&#xff09;协议则是一种现代化的通信协议&#xff0c;用在工厂自动化系统中实现设备之间的数据交换和通信。PLC转OPC U…

TensorRT动态形状(Dynamic Shape)出错,官方demo+自己模型运行时出错

(2024.7.2) 使用TensorRT处理动态输入形状推理时出现的错误&#xff0c;本案基于官方demo文件&#xff0c;已解决&#xff1a; TensorRT版本10.0&#xff0c;官方例子使用的是这个https://github.com/NVIDIA/trt-samples-for-hackathon-cn/blob/master/cookbook/01-SimpleDem…

贝叶斯估计(1):期末大乱炖

写在前面&#xff01; 1 先验分布和后验分布 三种信息&#xff1a;总体信息、样本信息、先验信息 总体信息&#xff1a;“总体是正态分布”&#xff1b;样本信息&#xff1a;总体抽取的样本提供的信息&#xff0c;是最新鲜的信息&#xff1b;先验信息&#xff1a;在抽样之前就…

Excel数据截取及合并多行多列数据

公式一&#xff1a;RIGHT(A2,LEN(A2)-FINDB(")",A2)) 公式二&#xff1a;PHONETIC(C2:D19) 详情可以看附件。

AJAX快速入门(一) express框架的安装和使用范例

主打一个有用 首先保证安装了nodejs环境 打开终端 初始化npm npm init安装express npm i express测试样例 目录结构 样例代码 express.js //引入express const express require(express);//创建应用对象 const app express();//创建路由规则 //req是请求对象&#x…

一文清晰了解HTML——简单实例

想要仿照该页面编写HTML代码&#xff1a; 在vscode中输入&#xff01;自动生成默认模板&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevic…

第二证券股市资讯:深夜!突然暴涨75%!

一则重磅收买引发医药圈轰动。 北京时间7月8日晚间&#xff0c;美股开盘后&#xff0c;美国生物制药公司Morphic股价一度暴升超75%。音讯面上&#xff0c;生物医药巨子礼来公司官宣&#xff0c;将以57美元/股的价格现金收买Morphic&#xff0c;较上星期五的收盘价溢价79%&…