1. 程式人生 > >【詳解】平面中最接近點對問題

【詳解】平面中最接近點對問題

陣列當中的最接近點對問題詳解

在一個一維陣列當中要找出兩個值相減得到的差值最小,當然有的人會說這個很簡單,只要將陣列當中的值兩兩相減,然後對最後的結果找一個最小值就可以了,這種方法想必是可以,這就是所謂的暴力解法,時間複雜度是非常大的。
我們這裡我介紹一種時間複雜度比較低的演算法,我們利用分治的策略,將大規模的問題縮小化,首先我們將陣列平分一半,左邊的數值均小於右邊的數值,但是左邊和右邊分別是亂序的,不需要排序,然後找到左邊的最接近點的值,右邊最接近點的值,當然還有可能左邊的最大值和右邊的最小值的差也可能是最小值,將這三個值作比較取最小值也就是最接近點的差值,有人會問那麼左邊和右邊的最小值怎麼計算呢,這就是分治的妙處所在了,左邊和右邊跟原問題是一樣了的,只是規模減小了,因此方法一樣,程式碼一樣,使用遞迴就可實現。
程式碼


const int MAXDISTENT = 0x7fffffff;
int Select_K(int *ar, int left,int right, int k);
int FindMax(int *ar, int left, int right)
{
    int Max = ar[left];
    for (int i = left; i < right; i++)
    {
        if (Max < ar[i])
        {
            Max = ar[i];
        }
    }
    return Max;
}
int
FindMin(int *ar, int left, int right) { int Min = ar[left]; for (int i = left; i < right; i++) { if (Min > ar[i]) { Min = ar[i]; } } return Min; } int MyMin(int a, int b) { return a > b ? b : a; } int MyMin(int a, int b, int c) { return
MyMin(MyMin(a, b), c); } int Cpair(int *ar, int left, int right) { if (right - left <= 0) return MAXDISTENT; int m = (right - left + 1)/2; Select_K(ar, left, right, m); int d1 = Cpair(ar, left, left+m - 1);//記得加left int d2 = Cpair(ar, left+m, right); int LMax = FindMax(ar, left, left+m - 1); int RMin = FindMin(ar, left+m, right); return MyMin(d1, d2, RMin-LMax); } int Cpair(int *ar, int len) { if (ar == NULL || len < 2) return MAXDISTENT; return Cpair(ar, 0, len - 1); }

平面中最接近點對問題詳解

平面中最接近點對的求解和上述的思想是一樣的,還是採用分治的思想,但是處理起來就比較麻煩了,主要是左邊和右邊的點的距離的計算,中線的劃分就按照x排個序取中就是中線,y軸上是同樣的道理,縱向的三根線圍起來的部分之間的點距離均大於左邊和右邊的最小距離d,而6個小方格里面,每個小方格里面最多一個點,因此,每個點只需要和這方格里面的點算距離就是最小距離
這裡寫圖片描述
程式碼

#define MAXFLOAT 3.14e38f;

struct Point_X
{
    float x;
    float  y;
    int id;
    operator float()const{ return x; }
};
struct Point_Y
{
    float x;
    float  y;
    int p;
    operator float()const { return y; }
};
/////////////////////////////////////
template<class Type>
void Merge(Type *sd, Type *si, int left, int m, int right)
{
    int i = left, j = m + 1;
    int k = left;
    while (i <= m && j <= right)
    {
        sd[k++] = si[i] < si[j] ? si[i++] : si[j++];
    }
    while (i <= m)
    {
        sd[k++] = si[i++];
    }
    while (j <= right)
    {
        sd[k++] = si[j++];
    }
}
template<class Type>
void Copy(Type *sd, Type *si, int left, int right)
{
    for (int i = left; i <= right; ++i)
    {
        sd[i] = si[i];
    }
}
template<class Type>
void MergePass(Type *br, Type *ar, int left, int right)
{
    if (left < right)
    {
        int m = (right - left) / 2 + left;
        MergePass(br, ar, left, m);
        MergePass(br, ar, m + 1, right);
        Merge(br, ar, left, m, right);
        Copy(ar, br, left, right);
    }

}
template<class Type>
void MergeSort(Type *ar, int n)
{
    Type *br = new Type[n];
    MergePass(br, ar, 0, n - 1);
    delete[]br;
}
///////////////////////////////////


void PrintPoint(Point_X *point, int num)
{
    for (int i = 0; i < NUM; i++)
    {
        cout <<point[i].id<<"=》"<< point[i].x << setw(6) << point[i].y << endl;
    } 
    cout << endl;
}
template<typename type>
float Distance(const type &p1,const type &p2)
{
    float dx = p1.x - p2.x;
    float dy = p1.y - p2.y;
    return sqrt(dx*dx + dy*dy);
}
float Closest(Point_X*X, Point_Y* Y, Point_Y*Z, int left,int right, Point_X&a, Point_X&b)
{
    int num = right - left;
    if (num <= 0)
        return MAXFLOAT;
    if (num == 1)
    {

        a = X[left];
        b = X[right];
        return Distance(X[left], X[right]);
    }
    if (num == 2)
    {
        float dis1=Distance(X[left], X[left+1]);
        float dis2 = Distance(X[left + 1], X[left + 2]);
        float dis3 = Distance(X[left + 2], X[left]);
        if (dis1 < dis2&&dis1 < dis3)
        {
            a = X[left];
            b = X[left+1];
            return dis1;
        }
        if (dis2 < dis1&&dis2 < dis3)
        {
            a = X[left+1];
            b = X[left+2];
            return dis2;
        }
        else
        {
            a = X[left];
            b = X[left+2];
            return dis3;
        }
    }
    int m = (right - left) / 2 + left;
    //int z1 = left, m1 = m + 1;

    float d1 = Closest(X, Y,Z, left, m, a, b);
    Point_X a1, b1;
    float d2 = Closest(X, Y, Z, m + 1, right, a1, b1);
    float d = MAXFLOAT;
    if (d1 < d2)
        d = d1;
    else
    {
        d = d2;
        a = a1;
        b = b1;
    }
    //Merge(Y, Z, left, m, right);
    int k = left;
    for (int i = left; i < right;i++)
    {
        if (fabs(X[m].x - Y[i].x) < d)
        {
            Z[k++] = Y[i];
        }
    }
    for (int i = left; i < k; i++)
    {
        for (int j = i + 1; j < k; j++)
        {
            if (fabs(Z[i].y - Z[j].y) >= d)//Z中本來就是按照Y排序的,因此一旦遇到比d大的後面也就都比d大
                break;
            float d3 = Distance(Z[i], Z[j]);
            if (d3 > d)
            {
                a = X[Z[i].p];
                b = X[Z[j].p];
                d = d3;
            }
        }
    }
    return d;
}
float Cpair(Point_X*X, int num, Point_X &a, Point_X &b)
{
    MergeSort(X, num);
    Point_Y *Y =new Point_Y[num];
    for (int i = 0; i < num; i++)
    {
        Y[i].p = i;
        Y[i].x = X[i].x;
        Y[i].y = X[i].y;
    }
    Point_Y *Z = new Point_Y[num];
    MergeSort(Y, num);
    float mindis = Closest(X, Y, Z, 0,num-1, a, b);
    return mindis;
}