凸包问题的分治思想
1.把给定点集中的点在横坐标方向上按照大小排序。如图1所示,
p1
p
1
和
pn
p
n
必是凸多边形的两个顶点。
2.在上凸包点集合
S1
S
1
中找到一个距离直线最远的点
pmax
p
m
a
x
,如图2所示。显然直线段
p1
p
1
pmax
p
m
a
x
与直线段
pn
p
n
pmax
p
m
a
x
把点集
S1
S
1
分成了3个集合。由凸包的性质可知
p1
p
1
,
pmax
p
m
a
x
,
pn
p
n
三点围成的三角形中的点不可能作为凸包的顶点,所以只考虑直线
p1pmax
p
1
p
m
a
x
左边的点
S11
S
11
以及直线
pmaxpn
p
m
a
x
p
n
左边的点
S12
S
12
,如图3所示。
3.递归求解得到凸多边形的边。
4.合并这些点即得所求凸包。
凸包问题的分治算法
void DealWithLeft(int first, int final, Point *array, Point *result)
{
int max = 0, index = -1;
int i = first;
if (first<final) //array[first]->array[final]射线左侧
{
for (i; i<final; i++)
{
int x1 = array[first].x, y1 = array[first].y;
int x2 = array[final].x, y2 = array[final].y;
int x3 = array[i].x, y3 = array[i].y;
int compute = x1*y2 + x3*y1 + x2*y3 - x3*y2 - x2*y1 - x1*y3;
if (compute > max)
{
max = compute;
index = i;
}
}
}
else
{
for (i; i >= 0; i--)//array[final]->array[first]射线左侧
{
int x1 = array[first].x, y1 = array[first].y;
int x2 = array[final].x, y2 = array[final].y;
int x3 = array[i].x, y3 = array[i].y;
int compute = x1*y2 + x3*y1 + x2*y3 - x3*y2 - x2*y1 - x1*y3;
if (compute > max)
{
max = compute;
index = i;
}
}
}
if (index != -1) //取到array[index](即最高点) ,这个点与array[final]、array[first]构成的三角形面积最大
{
Add(array, result, index);
DealWithLeft(first, index, array, result);
DealWithLeft(index, final, array, result);
}
}
算法复杂度
快包的效率与快速排序的效率相同,平均情况下是 O(nlog2n) O ( n l o g 2 n ) ,最坏情况下是 O(n2) O ( n 2 ) 。
源码
#include "stdafx.h"
#include<stdio.h>
typedef struct {
int x, y;
}Point;
int arrayLength = 0; //数组长度
//初始化测试数据,返回一个数组
void InitialData(Point *array)
{
FILE *fp = freopen("input.txt", "r", stdin);
char ch;
Point* currentPoint = array;
for (int i = 0; ch != EOF; i++)
{
scanf_s("%d%d", &array[i].x, &array[i].y);
ch = fgetc(fp);
arrayLength++;
}
fclose(fp);
}
//交换位置
void swap(Point *array, int i, int j)
{
if (i<j)
{
int x = 0, y = 0;
x = array[i].x;
y = array[i].y;
array[i].x = array[j].x;
array[i].y = array[j].y;
array[j].x = x;
array[j].y = y;
}
}
//返回分裂位置
int Partition(Point *array, int l, int r)
{
int p = l, i = l, j = r + 1;
do
{
do
{
i++;
} while (array[i].x<array[p].x);
do
{
j--;
} while (array[j].x>array[p].x);
swap(array, i, j);
} while (i<j);
swap(array, i, j);
swap(array, p, j);
return j;
}
//按x坐标的值从大到小排序,这里用的是快排
void Qsort(Point *array, int l, int r)
{
int s = 0;
if (l<r)
{
s = Partition(array, l, r); //找到分裂位置
Qsort(array, l, s - 1);
Qsort(array, s + 1, r);
}
}
//打印数组
void Put(Point *array, int length)
{
for (int i = 0; i<length; i++)
{
printf_s("%d\t%d\n", array[i].x, array[i].y);
}
printf_s("\n");
}
int resultCount = 0; //result[15]里面插入值的个数
//向result数组里面添加array数组首尾项
void Add(Point *array, Point *result)
{
result[resultCount].x = array[0].x;
result[resultCount++].y = array[0].y;
result[resultCount].x = array[14].x;
result[resultCount++].y = array[14].y;
}
//向result数组里面添加 array[i]
void Add(Point *array, Point *result, int i)
{
result[resultCount].x = array[i].x;
result[resultCount++].y = array[i].y;
}
//处理array[first]->array[final]射线左侧的点
void DealWithLeft(int first, int final, Point *array, Point *result)
{
int max = 0, index = -1;
int i = first;
if (first<final) //array[first]->array[final]射线左侧
{
for (i; i<final; i++)
{
int x1 = array[first].x, y1 = array[first].y;
int x2 = array[final].x, y2 = array[final].y;
int x3 = array[i].x, y3 = array[i].y;
int compute = x1*y2 + x3*y1 + x2*y3 - x3*y2 - x2*y1 - x1*y3;
if (compute > max)
{
max = compute;
index = i;
}
}
}
else
{
for (i; i >= 0; i--)//array[final]->array[first]射线左侧
{
int x1 = array[first].x, y1 = array[first].y;
int x2 = array[final].x, y2 = array[final].y;
int x3 = array[i].x, y3 = array[i].y;
int compute = x1*y2 + x3*y1 + x2*y3 - x3*y2 - x2*y1 - x1*y3;
if (compute > max)
{
max = compute;
index = i;
}
}
}
if (index != -1) //取到array[index](即最高点) ,这个点与array[final]、array[first]构成的三角形面积最大
{
Add(array, result, index);
DealWithLeft(first, index, array, result);
DealWithLeft(index, final, array, result);
}
}
void GetResult(Point *array, Point *result)
{
Add(array, result); //将首尾两个点并入result[15]
DealWithLeft(0, 14, array, result); //处理array[0]->array[14]射线左边的点
DealWithLeft(14, 0, array, result); //处理array[14]->array[0]射线右边的点
}
int _tmain(int argc, _TCHAR* argv[])
{
Point array[15], result[15];
//初始化数组数据
InitialData(array);
printf_s("输入数据:\n\n");
Put(array, arrayLength);
//给数组排序,按x从小到大
Qsort(array, 0, arrayLength - 1);
printf_s("排序后的数据:\n\n");
Put(array, arrayLength);
//获取result数组,存放所有结果顶点
GetResult(array, result);
printf_s("结果集:\n\n");
Put(result, resultCount);
getchar();
return 0;
}