算法复杂度最低界限LowBound
算法求解复杂度是否存在一个最低界限,有时候想尽一切办法优化一个算法,去优化其复杂度,比如
清华计算几何-ConvexHull(凸包)-求极点InTriangle/ToLeft Test-CSDN博客
清华计算几何-ConvexHull(凸包)-求极边_计算几何中的toleft测试-CSDN博客
清华计算几何-ConvexHull(凸包)-JarivsMarch-CSDN博客
算法复杂度依次到: O(n4), O(n3), O(n2)
一直优化下去能最低到哪个复杂度,有时候不好直接估算出。有个比较好的办法是从经典算法(比如排序)中,找到此算法和经典算法的等价转换。如果此算法和经典算法等价,则此算法的LowBound也是经典算法的LowBound。
算法等价转换
算法等价转换就是: 假设A为经典算法, 已经知道其算法复杂度。存在另外一个未知算法复杂度的算法B,如今要估算算法B的lowBond. 如果A算法的输入经过O(n)以内复杂度转换算法变为B算法输入,B算法计算的输出能在O(n)复杂度以内转换为A算法的输入,则称A算法和B算法等价。
估算ConvexHull算法的LowBound
从前面可以隐隐看出点集求解ConvexHull的流程和排序类似, 建立起排序和凸包的点集合之间O(n)的转换关系(点输入和点输出),排序的算法Lowbound就是凸包算法的Lowbound。
排序的LowBound是O(nlogn), 而等价下凸包算法也是O(nlogn)
凸包算法-GrahamScan
GrahamScan算法流程
Presorting(预排序)
[1]找到LowertToLeft点P
[2]找到和P点相连最右边的点(逆时针CCW)
Scan(扫描)
GrahamScan算法Backtrack执行案例
GrahamScan算法代码实现
算法核心代码
#include <iostream>
#include <vector>
#include <stack>
#include <list>
#include <algorithm>
#include "BasicCompute.h"
using namespace std;
template<typename type>
class CustomStack
{
private:
vector<type> datas;
public:
CustomStack()
{
}
void Push(const type& value)
{
datas.push_back(value);
}
type Pop()
{
int num = GetNum();
if (num == 0)
throw "size is zero, do not allow pop";
int value = datas[num - 1];
datas.pop_back();
return value;
}
int GetNum()
{
return datas.size();
}
type GetTopValue(int topIndex = 0)
{
return datas[GetNum() - topIndex - 1];
}
void GetVectorData(vector<type>& outDatas)
{
outDatas = datas;
}
};
void GrahamScan_GetConvexPointSet(const vector<Point>& inPoints, vector<int>& convecHullPointIndexs)
{
if (inPoints.size() <= 3)
return;
convecHullPointIndexs.empty();
int ltfIndex = FindLowestThenLeftmost(inPoints);
// quick sort by to left
vector<int> tArray;
for (int index = 0; index < inPoints.size(); index++)
{
if (index != ltfIndex)
tArray.push_back(index);
}
auto CompareFunc = [&](int a, int b)
{
return IsLeft(inPoints[ltfIndex], inPoints[a], inPoints[b]);
};
sort(tArray.begin(), tArray.end(), CompareFunc);
// init stack s and stack t
CustomStack<int> s;
s.Push(ltfIndex);
s.Push(tArray[0]);
CustomStack<int> t;
for (int index = tArray.size() - 1; index >= 1; index--)
{
t.Push(tArray[index]);
}
while (t.GetNum() != 0)
{
int sTopIndex = s.GetTopValue(0);
int sTopSecondIndex = s.GetTopValue(1);
int tTopIndex = t.GetTopValue(0);
if (IsLeft(inPoints[sTopSecondIndex], inPoints[sTopIndex], inPoints[tTopIndex]))
{
s.Push(t.Pop());
}
else
{
s.Pop();
}
}
s.GetVectorData(convecHullPointIndexs);
}
测试代码
#include <iostream>
#include <vector>
#include "ExtremityEdgeConvex.h"
#include "JarvisMarch.h"
#include "GrahamScan.h"
using namespace std;
int main()
{
std::cout << "Hello World!\n";
// point set contruct
vector<Point> inPoints =
{
{0, 0},
{-1, -1},
{5, 2},
{4, 5},
{3, 3},
{-1, 3},
{2, 2},
{-3, 2},
};
vector<int> convecHullPointIndexs;
GrahamScan_GetConvexPointSet(inPoints, convecHullPointIndexs);
for (int index = 0; index < convecHullPointIndexs.size(); index++)
{
int pointIndex = convecHullPointIndexs[index];
printf("(%f, %f)\n", inPoints[pointIndex].x, inPoints[pointIndex].y);
}
}
测试结果
GrahamScan算法复杂度
PreProcesing:LTL复杂度O(n), Presorting是快排O(nlogn)
San扫描: Scan经过的路径是一个平面图(PlanarGraph), N个顶点的平面图至多拥有3N条边,也就是San算法复杂度不可能超过3N。也就是算法复杂度为O(n).
综上GrahamScan算法复杂度为O(nlogn)。
参考资料
[1]清华计算几何 P31-P48