Skip to content

implemented graham scan in C plus plus #616

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jul 10, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
102 changes: 102 additions & 0 deletions contents/graham_scan/code/c++/graham_scan.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#include<iostream>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please space between #include and header in order to be consistent with other implementations in AAA.

#include<vector>

struct point {
double x;
double y;
};

point origin;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use a global origin variable. Localize it in grahamScan and as argument of ccwCheck.

Suggested change
point origin;


void swap(point &p1, point &p2){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a swap function swap<Type>(Type& left, Type& right) in <algorithm> header. Don't make your own.

point temp = p1;
p1 = p2;
p2 = temp;
}

int distSq(point p1, point p2){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As compare is useless, and it only uses in compare, this should also be useless.

return (p1.x - p2.x)*(p1.x - p2.x) +(p1.y - p2.y)*(p1.y - p2.y);
}

int ccwCheck(point a, point b, point c){

int crossProduct = (b.y - a.y) * (c.x - b.x) - (b.x - a.x) * (c.y - b.y);
if (crossProduct == 0)
{
return 0;
}
return ( crossProduct > 0)? -1 : 1;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's unnececcary to return 1,0 or -1. It's not a bool, so we have to compare it again, therefore, why not just return it?
Also, the previous if is useless, too.

Suggested change
return ( crossProduct > 0)? -1 : 1;
return (b.y - a.y) * (c.x - b.x) - (b.x - a.x) * (c.y - b.y);

}

int compare(const void *vp1, const void *vp2){
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The compare function is totally useless, I think. It's OK to have points colinear, as the one inwards will be replaced.


point *p1 = (point *)vp1;
point *p2 = (point *)vp2;

int o = ccwCheck(origin, *p1, *p2);
if (o == 0)
return (distSq(origin, *p2) >= distSq(origin, *p1))? -1 : 1;

return (o == 1)? -1: 1;
}

void print(std::vector<point> points)
{
std::cout << "the points of hull are as follows:\n";
for (size_t i = 0; i < points.size(); i++) {
std::cout << "(" << points[i].x << "," << points[i].y << ")\n";
}
}

std::vector<point> grahamScan(std::vector<point> points){
//selecting origin(the element with y minimum)
double yMin = points[0].y;
int min = 0;
for (size_t i = 1; i < points.size(); i++) {
double y = points[i].y;
if ((y < yMin) || (yMin == y && points[i].x < points[min].x)){
yMin = points[i].y;
min = i;
}
}
swap(points[0], points[min]);
origin = points[0];

//sorting by polar angle and removing duplicates
qsort(&points[1], points.size() - 1, sizeof(point), compare);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer to use sort<_Ranit, _Pr>(const _RanIt _First, const _RanIt _Last, _Pr _Pred) in <algorithm> instead of qsort(void* _Base, size_t _NumOfElements, size_t _SizeOfElements, int (*_CompareFunction)(const void*, const void*) in <corecrt_search.h>.
Also, since compare is useless, the _Pr can be a simple lambda [&](point& a, point& b){return ccwCheck(points[0],a,b)

std::vector<point> pointsSorted;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't needed to create new one. original one is OK to edit

pointsSorted.push_back(origin);
for(size_t i = 1; i < points.size(); i++){
while(i < points.size() - 1 && ccwCheck(origin, points[i],
points[i+1]) == 0){
i++;
}
pointsSorted.push_back(points[i]);
}

//creating the convex hull
std::vector<point> hull;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned, the algorithm shouldn't create a new vector to store hull.

To save memory and expensive append() operations, we ultimately look for points that should be on the hull and swap them with the first elements in the array. If there are Melements on the hull, then the first M elements in our output random distribution of points will be the hull.

hull.push_back(points[0]);
hull.push_back(points[1]);
hull.push_back(points[2]);
for (size_t i = 3; i <= pointsSorted.size(); i++) {
while (ccwCheck(hull.at(hull.size()-2), hull.at(hull.size()-1),
pointsSorted[i]) != 1)
{
hull.pop_back();
}
hull.push_back(points[i]);
}
return hull;
}

int main()
{
std::vector<point> points = {{-5, 2}, {5, 7}, {-6, -12}, {-14, -14}, {9, 9},
{-1, -1}, {-10, 11}, {-6, 15}, {-6, -8}, {15, -9},
{7, -7}, {-2, -9}, {6, -5}, {0, 14}, {2, 8}};

std::vector<point> hull = grahamScan(points);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like a printing statment before finding hull, in order to show the points.

print(hull);
return 0;
}
6 changes: 6 additions & 0 deletions contents/graham_scan/graham_scan.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ We can find whether a rotation is counter-clockwise with trigonometric functions
[import:13-15, lang:"go"](code/golang/graham.go)
{% sample lang="java" %}
[import:27-29, lang:"java"](code/java/GrahamScan.java)
{% sample lang="cpp" %}
[import:21-29, lang="cpp"](code/c++/graham_scan.cpp)
{% endmethod %}

If the output of this function is 0, the points are collinear.
Expand Down Expand Up @@ -54,6 +56,8 @@ In the end, the code should look something like this:
[import:21-42, lang:"go"](code/golang/graham.go)
{% sample lang="java" %}
[import:35-70, lang:"java"](code/java/GrahamScan.java)
{% sample lang="cpp" %}
[import:51-91, lang="cpp"](code/c++/graham_scan.cpp)
{% endmethod %}

### Bibliography
Expand All @@ -77,6 +81,8 @@ In the end, the code should look something like this:
[import, lang:"go"](code/golang/graham.go)
{% sample lang="java" %}
[import, lang:"java"](code/java/GrahamScan.java)
{% sample lang="cpp" %}
[import, lang="cpp"](code/c++/graham_scan.cpp)
{% endmethod %}

<script>
Expand Down