算法复习-分治法(最近点对,凸包问题)

本文介绍了使用分治法解决最近点对和凸包问题的思路。在最近点对问题中,通过点的x轴和y轴坐标排序,先找到分割点,再递归地解决左右子问题,最后比较最小距离。而在凸包问题中,通过分治策略,不断找到距离某一基准直线最远的点,最终形成凸包。整个过程涉及点到直线的距离计算,时间复杂度为O(n㏒n)。
摘要由CSDN通过智能技术生成

分治法:

分治法在每一层递归上都有三个步骤:

分解:将原问题分解为若干个规模较小,相互独立,与原问题形式相同的子问题;

解决:若子问题规模较小而容易被解决则直接解,否则递归地解各个子问题;

合并:将各个子问题的解合并为原问题的解。


实例:

1:求最近点对:

  在二维空间里,可用分治法求解最近点对问题。预处理:

分别根据点的x轴和y轴坐标进行排序,得到X和Y,很显然此时X和Y中的点就是S中的点。

情况(1):点数小于等于三时: 


情况(2):点数大于三时:

     首先划分集合S为SL和SR,使得集合分为左右两边,尽量使L分割出两个相同点数的集合。然后在SL与SR中解决最近点对问题,得出SL中最近点对距离为DL,SR中最近点对距离为DR,然后D=MIN(DL,DR),再用D与[L-D,L+D]间的最近点对(P1,P2)作比较。得出较小的点对,即为S中的最近点对。


具体可以分为以下步骤:

步骤1:根据点的y值和x值对S中的点排序。

步骤2:找出中线L将S划分为SL和SR

步骤3:将步骤2递归的应用解决SL和SR的最近点对问题,并令d=min(dL,dR)。

步骤4:将L-d~L+d内的点以y值排序,对于每一个点(x1,y1)找出y值在y1-d~y1+d内的所有点,计算距离为d’。如果d'小于d,令d=d',最后的d值就是答案。



2:凸包问题:

时间复杂度:O(n㏒n)。 

思路:应用分治法思想,把一个大问题分成几个结构相同的子问题,把子问题再分成几个更小的子问题……。然后我们就能用递归的方法,分别求这些子问题的解。最后把每个子问题的解“组装”成原来大问题的解。 


步骤:

1:把所有的点都放在二维坐标系里面。那么横坐标最小和最大的两个点 P1 和 Pn 一定是凸包上的点。直线 P1Pn 把点集分成了两部分,即 X 轴上面和下面两部分,分别叫做上包和下包。

2:对上包:求距离直线 P1Pn 最远的点,即下图中的点 Pmax 。

3:作直线 P1Pmax 、PnPmax,把直线 P1Pmax 左侧的点当成是上包,把直线 PnPmax 右侧的点也当成是上包。

4:重复步骤 2、3。

5:对下包也作类似操作。 



点到直线距离: 

设有一个点 P3 和直线 P1P2 。(坐标:p1(x1,y1),p2(x2,y2),p3(x3,y3)) 

对上式的结果取绝对值,绝对值越大,则距离直线越远。

注意:在步骤一,如果横坐标最小的点不止一个,那么这几个点都是凸包上的点,此时上包和下包的划分就有点不同了,需要注意

#include<iostream>
#include<stdlib.h>
#include <time.h>
#include <vector>
#include <math.h>
#include <stdio.h>
#include <sys/timeb.h>
#include <ctime>
#include <climits>

using namespace std;
#define TOP  100
#define BOTTOM  -100
static int number;
class ThreeDimensionalPoint{
    public :
    
    ThreeDimensionalPoint(double x1, double y1, double z1){
        
        x = x1;
        y = y1;
        z = z1;
    }
    
    void print(){
        cout << "x:" << x << " y:" << y << " z:" << z<<endl;
    }
    double x;
    double y;
    double z;
};

vector<ThreeDimensionalPoint *>* GetDataSet(int NUM_DATA,int Max = 100, int Min = -100){
    
    vector<ThreeDimensionalPoint *>* DataSet = new vector<ThreeDimensionalPoint *>();
    srand((unsigned)time(NULL));
    int RANDNUM = Max - Min;
    for (int i = 0; i < NUM_DATA; i++){
        double X_DATA = rand() % RANDNUM - RANDNUM / 2;
        double Y_DATA = rand() % RANDNUM - RANDNUM / 2;
        double Z_DATA = rand() % RANDNUM - RANDNUM / 2;
        ThreeDimensionalPoint* randpoint = new ThreeDimensionalPoint(X_DATA, Y_DATA, Z_DATA);
        DataSet->push_back(randpoint);
    }
    vector<ThreeDimensionalPoint *>* FinalDataSet = new vector<ThreeDimensionalPoint *>();
    int num = DataSet->size();
    
    double *p = new double[num];
    int *q = new int[num];
    
    for (int i = 0; i < num; i++){
        q[i] = i;
    }
    
    for (int i = 0; i < num; i++){
        p[i] = DataSet->at(i)->x;
    }
    
    for (int i = 1; i < num; i++)    //从第2个数据开始插入
    {
        int j = 0;
        while (j < i && p[j] <= p[i])    //寻找插入的位置
            j++;
        
        if (j < i)    //i位置之前,有比pDataArray[i]大的数,则进行挪动和插入
        {
            int k = i;
            int temp = q[i];
            double temp1 = p[i];
            while (k > j)    //挪动位置
            {
                p[k] = p[k - 1];
                q[k] = q[k - 1];
                k--;
            }
            q[k] = temp;    //插入
            p[k] = temp1;
        }
    }
    
    for (int i = 0; i < num; i++){
        FinalDataSet->push_back(DataSet->at(q[i]));
    }
    return FinalDataSet;
}

double min(double num1, double num2){
    
    if (num2 < num1)
        num1 = num2;
    return num1;
}

double max(double num1, double num2){
    if (num2 > num1)
        num1 = num2;
    return num1;
}

double Threepartition(double x1, double x2, double x3){
    double Split_X = x1;
    if (x1 > x2 || x1 == x2){
        if (x1 > x3 || x1 == x3){
            if (x2 > x3 || x2 == x3)
                Split_X = x2;
            else
                Split_X = x3;
        }
        else{
            Split_X = x1;
        }
        
    }
    else{
        if (x2 > x3 || x2 == x3){
            if (x1 > x3 || x1 == x3)
                Split_X = x1;
            else
                Split_X = x3;
        }
        else{
            Split_X = x2;
        }
        
    }
    return Split_X;
}

//不开平方,方便比较节省效率
double Distance_N(ThreeDimensionalPoint* point1, ThreeDimensionalPoint* point2){
    
    return pow((point1->x - point2->x), 2) + pow((point1->y - point2->y), 2) + pow((point1->z - point2->z), 2);
    
}

//开平方,获得实际距离
double Distance_Y(ThreeDimensionalPoint* point1, ThreeDimensionalPoint* point2){
    
    return sqrt(pow((point1->x - point2->x), 2) + pow((point1->y - point2->y), 2) + pow((point1->z - point2->z), 2));
    
}

//蛮力法解决最近点对问题
double Brute_PointDistance(vector<ThreeDimensionalPoint *>* SUBSET){
    
    double PointDistance = double(INT_MAX);
    
    for (int i = 0; i < SUBSET->size(); i++){
        for (int j = i + 1; j < SUBSET->size(); j++){
            double Distance = Distance_N(SUBSET->at(i), SUBSET->at(j));
            
            if (Distance < PointDistance)
                PointDi
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值