【算法学习】快包算法

#include "stdafx.h"
#include <stdio.h>
#include <windows.h>
#include <stdlib.h>
#include <math.h>

坐标数据结构:
typedef struct
{
	float x;
	float y;
}Point;
typedef bool(*Func)(Point, Point, Point);

判断点test是否在a,b组成的直线的上方:
bool Upstair(Point a, Point b, Point test)
{
	double k, d;
	if (fabs(a.x - test.x) < 0.000001 && fabs(a.y - test.y) < 0.000001
		|| fabs(b.x - test.x) < 0.000001 && fabs(b.y - test.y) < 0.000001)
		return false;
	/* 此时test点肯定不符合结果(联想在main函数寻找a,b点时的要求) */
	if (fabs(b.x - a.x) < 0.000001)
		return false;
	k = (b.y - a.y) / (b.x - a.x);
	d = a.y - k * a.x;
	double ret = k*test.x - test.y + d;
	if (ret < -0.000001)	//点在上方
		return true;
	else   //点在线上 和 点在线的下方 
		return false;
}
判断点test是否在a,b组成的直线的下方:
bool Downstair(Point a, Point b, Point test)
{
	double k, d;
	if (fabs(a.x - test.x) < 0.000001 && fabs(a.y - test.y) < 0.000001
		|| fabs(b.x - test.x) < 0.000001 && fabs(b.y - test.y) < 0.000001)
		return false;
		/* 此时test点肯定不符合结果(联想在main函数寻找a,b点时的要求) */
	if (fabs(b.x - a.x) < 0.000001)
		return false;
	k = (b.y - a.y) / (b.x - a.x);
	d = a.y - k * a.x;
	double ret = k*test.x - test.y + d;
	if (ret > 0.000001)	//点在下方
		return true;
	else   //点在线上 和 点在线的上方 
		return false;
}

查找符合的点,递归函数:
void FindPoint(Point point[], int plen, Point a, Point b, Func func)
{
	if (plen == 0)
		return;
	/* 找出 Pmax 点 */
	Point pmax;
	pmax.x = point[0].x;
	pmax.y = point[0].y;
	double k, d;
	k = (b.y - a.y) / (b.x - a.x);
	d = a.y - k * a.x;
	double dist = fabs(k*pmax.x - pmax.y + d);	
	double newdist;
	for (int i = 1; i < plen; ++i)
	{
		newdist = fabs(k*point[i].x - point[i].y + d);
		if (newdist - dist > 0.000001)
		{
			pmax.x = point[i].x;
			pmax.y = point[i].y;
		}
		else if (fabs(newdist - dist) < 0.000001)
		{	//选择使角PmaxPaPb最大的点
			double k_pmax = (pmax.y - a.y) / (pmax.x - a.x);
			double k_point = (point[i].y - a.y) / (point[i].x - a.x);
			if (fabs(k_point - k) - fabs(k_pmax - k) > 0.000001)
			{
				pmax.x = point[i].x;
				pmax.y = point[i].y;
			}
		}
	}
	printf("Point(%f, %f)\n", pmax.x, pmax.y);

	/* 找出各自符合满足 Pmax,Pa 和 Pmax,Pb 的点 */
	Point *p1 = (Point *)malloc((plen / 2 + 1)*sizeof(Point));
	Point *p2 = (Point *)malloc((plen / 2 + 1)*sizeof(Point));
	int p1idx = 0, p2idx = 0;
	for (int i = 0; i < plen; ++i)
	{
		if (func(pmax, a, point[i]))
		{
			p1[p1idx].x = point[i].x;
			p1[p1idx].y = point[i].y;
			p1idx++;
		}
		else if (func(pmax, b, point[i]))
		{
			p2[p2idx].x = point[i].x;
			p2[p2idx].y = point[i].y;
			p2idx++;
		}
	}
	/* 递归寻找Pmax */
	FindPoint(p1, p1idx, pmax, a, func);
	FindPoint(p2, p2idx, pmax, b, func);

	free(p1);
	free(p2);

}主函数:
int _tmain(int argc, _TCHAR* argv[])
{
	int number = 0;
	while (number < 3)
	{
		printf("How much Point do you input(>=3):\n");
		scanf("%d", &number);
	}
	printf("Please input Point:\n");

	Point *p = (Point *)malloc(number * sizeof(Point));
	for (int i = 0; i < number; ++i)
	{
		scanf("%f%f", &(p[i].x), &(p[i].y));
	}

	/* 找出两个顶点 */
	Point a, b;
	a.x = p[0].x;
	a.y = p[0].y;
	b.x = p[0].x;
	b.y = p[0].y;
	for (int i = 1; i < number; ++i)
	{
		if (a.x - p[i].x > 0.000001) //有比a更小的(横坐标)
		{
			a.x = p[i].x;
			a.y = p[i].y;
		}
		else if (fabs(a.x - p[i].x) < 0.000001)
		{ // 存在和a在同一竖直线上的点,此时应该选择最上或最下的点(这里选最上)
			if (p[i].y - a.y > 0.000001)
			{
				a.x = p[i].x;
				a.y = p[i].y;
			}
		}
		if (b.x - p[i].x < -0.000001)
		{
			printf("b change\n");
			b.x = p[i].x;
			b.y = p[i].y;
		}
		else if (fabs(b.x - p[i].x) < 0.000001)
		{ // 存在和b在同一竖直线上的点,此时应该选择最上或最下的点(这里选最下,和a对应,为了更可能的均分上包和下包点数)
			if (p[i].y - a.y < -0.000001)
			{
				b.x = p[i].x;
				b.y = p[i].y;
			}
		}
	}
	/* 所有点在一条竖直的线,则此时已是所求结果 */
	if (fabs(a.x - b.x) < 0.000001)
	{
		printf("Point(%f, %f)\n", a.x, a.y);
		printf("Point(%f, %f)\n", b.x, b.y);
		system("pause");
		return 0;
	}
	
	printf("Point(%f, %f)\n", a.x, a.y);
	printf("Point(%f, %f)\n", b.x, b.y);
	/* 划分点为两个集,即上包 和 下包*/
	Point *p1 = (Point *)malloc((number / 2 + 1)*sizeof(Point));
	Point *p2 = (Point *)malloc((number / 2 + 1)*sizeof(Point));
	int p1idx = 0, p2idx = 0;
	for (int i = 0; i < number; ++i)
	{
		if (Upstair(a, b, p[i]))
		{
			p1[p1idx].x = p[i].x;
			p1[p1idx].y = p[i].y;
			p1idx++;
		}
		else if (Downstair(a, b, p[i])) 
		{
			p2[p2idx].x = p[i].x;
			p2[p2idx].y = p[i].y;
			p2idx++;
		}
	}
	FindPoint(p1, p1idx, a, b, Upstair);
	FindPoint(p2, p2idx, a, b, Downstair);

	free(p);
	free(p1);
	free(p2);
	system("pause");
	_tmain(0, NULL);

	return 0;


算法分析:

在输入数中分上包和下包时时间复杂度为O(n),两个递归函数的调用规模下降为n/2.

故可得T(n) = 2T(n/2) + n,递归函数遍历元素时间复杂度为n,然后同样道理递归, 最好的效率是刚好上包和下包元素相等或差一的情况。

由T(n) = 2T(n/2) + n ,得T(2^k) = 2T(2^k-1) + 2^k

T(2^k) = 2(2T(2^k-2) + 2^k-1) + 2^k

  = 2^2T(2^k-2) + 2*2^k

  = 2^kT(1) + k*2^k

由T(1) = 1,k = logn

T(n) = n + logn*n

所以,时间复杂度是nlogn.




你可能感兴趣的:(算法,C语言)