zoukankan      html  css  js  c++  java
  • leetcode 587. Erect the Fence 凸包的计算

    leetcode.587.Erect the Fence

    凸包问题。好像是我在leetcode做的第一个凸包问题吧。

    第一次做,涉及到的东西还是蛮多的。有一个东西很重要,就是已知一个点和一个矢量,求这个点在这个矢量的左边还是右边。网上都是用一个点对于已知直线的位置的描述,用矢量代替直线更准确,直观。

    http://blog.csdn.net/modiz/article/details/9928955一个是用面积法描述的

    http://blog.csdn.net/modiz/article/details/9928553一个用矢量的叉积描述的

    其实我看上去感觉都是一样的貌似的。。其实都是用矢量的叉积描述的。

    定义:平面上的三点A(x1,y1),B(x2,y2),C(x3,y3)组成三角形的面积是:

    S(A,B,C)=|y1 y2 y3|= [(x1-x3)*(y2-y3)-(y1-y3)*(x2-x3)]/2

    这是用向量的混合积表示的面积,

    就像这样。。绘图潦草。。 这个求出来的面积应该就是这样的,x轴为矢量AB的方向,所以可以理解为x轴上面的面积为S,下面的面积为-S,所以S为负数,点C就在矢量AB的右侧,S为正数,C就在矢量AB的左侧。这样理解不是很直观嘛。


    然后我就写一下我的思路,也是从leetcode上的Sloution上借鉴的,Sloution也是来源于http://www.algorithmist.com/index.php/Monotone_Chain_Convex_Hull.cpp

    思路:

    1.把points按x轴上的坐标由小到大(有大到小也可以)进行排序,相等就按y轴

    2.先放入两个点,

    3.开始遍历points,每放入一个point         //为了构造凸包下面的边界    

    4.判断以栈顶两个元素组成的向量,判断这个点是否在向量的左侧或者线上,若是,则放入栈,若在右侧,出栈一个元素,重新执行步骤4;直到遍历完points

    5.反向遍历points,执行步骤3                  //为了构造凸包上面的边界

    6.去除栈中相同的points

    构造下面边界时的情况,当发现下一个点是C并且在栈顶AB的右侧,此时B出栈,构造向量OA,这里不画了,可以发现C在OA的左侧,所以C进栈。

    然后遍历到点D,构造向量AC,发现D就在AC的左侧,所以直接进栈。通过这种方式,一点一点确定边界范围。同理确定上面的边界的时候也是这个方式,自己手动画一下就很直观地明白了。

    贴一下AC代码吧,注释相当详细

     1 /**
     2  * Definition for a point.
     3  * struct Point {
     4  *     int x;
     5  *     int y;
     6  *     Point() : x(0), y(0) {}
     7  *     Point(int a, int b) : x(a), y(b) {}
     8  * };
     9  */
    10 class Solution {
    11 public:
    12     typedef int coord_t;
    13     //防止计算量太大
    14     typedef long long coord2_t;
    15     
    16     //通过判断向量的点积正负来确定点位于矢量左侧还是右侧    
    17     coord2_t cross(const Point &O,const Point &A,const Point &B){
    18         return (A.x-O.x)*(coord2_t)(B.y-O.y)-(A.y-O.y)*(coord2_t)(B.x-O.x);
    19     }
    20     
    21     //按x轴坐标排序,相等时用y轴坐标排序    
    22     static bool cmp(Point &p1,Point &p2){
    23         return p1.x<p2.x||(p1.x==p2.x&&p1.y<p2.y);
    24     }
    25     
    26     //去除duplicate的辅助函数    
    27     static bool equ(Point &p1,Point &p2){
    28         return p1.x==p2.x&&p1.y==p2.y;
    29     }
    30     
    31     vector<Point> outerTrees(vector<Point>& points) {
    32        int n=points.size(),k=0;
    33        //遍历两轮,所以size为2n
    34        //因为要用栈的顶部两个元素,所以用vector比要方便一些
    35        vector<Point> res(2*n);
    36        
    37        //排序
    38        sort(points.begin(),points.end(),cmp);
    39        
    40        //正向遍历
    41        for(int i=0;i<n;i++){
    42            //若在矢量的右侧,出栈
    43            while(k>=2&&cross(res[k-2],res[k-1],points[i])<0) k--;
    44            res[k++]=points[i];
    45        }
    46        
    47        //反向遍历
    48        for(int i=n-2,t=k+1;i>=0;i--){
    49            //若在矢量的右侧,出栈
    50            while(k>=t&&cross(res[k-2],res[k-1],points[i])<0) k--;
    51            res[k++]=points[i];
    52        }
    53        
    54        //重新resize并排序
    55        res.resize(k);
    56        sort(res.begin(),res.end(),cmp);
    57        //除去相同的元素,unique函数的方法是吧相同的元素都放到vector的尾部,并返回一个这些相同元素起始的iterator
    58        res.erase(unique(res.begin(),res.end(),equ),res.end());
    59        return res;
    60     }
    61 };

    另外删除vector中的重复数据(unique),这个技巧也是我刚学的。

    凸包有很多计算方法http://www.7zhang.com/index/cms/read/id/327304.html,这个博主讲了很多,我选了一个最好理解的写了。

    因为最近在学python,所以特地用python实现了一遍

     1 # Definition for a point.
     2 # class Point(object):
     3 #     def __init__(self, a=0, b=0):
     4 #         self.x = a
     5 #         self.y = b
     6 
     7 class Solution(object):
     8     def outerTrees(self, points):
     9         """
    10         :type points: List[Point]
    11         :rtype: List[Point]
    12         """
    13         def sign(p,q,r):
    14             #cmp函数比较a,b大小,a<b return -1,a==b return 0,a>b return 1
    15             return cmp((p.x-r.x)*(q.y-r.y),(p.y-r.y)*(q.x-r.x))
    16             
    17         def drive(hull,r):
    18             hull.append(r)
    19             # *hull[-3:]表示尾部三个元素
    20             while len(hull)>=3 and sign(*hull[-3:])<0:
    21                 hull.pop(-2);
    22             return hull;
    23         
    24         #lambda表示临时写的一个函数    
    25         points.sort(key=lambda p:(p.x,p.y))
    26         #reduce(f,list,可以作为计算的初始值)
    27         lower=reduce(drive,points,[])
    28         #points[::-1]表示反转list
    29         upper=reduce(drive,points[::-1],[]);
    30         return list(set(lower+upper))

    很多python的东西还不熟悉。。顺便熟悉一下。。

  • 相关阅读:
    [Axiom 3D]1.初识Axiom
    [.Net]System.OutOfMemoryException异常
    一个恶心的需求
    度分秒的正则表达式
    CSLA.Net学习(3)INotifyPropertyChanged和IDataErrorInfo
    [转载]高斯正反算
    分带?不分带?
    chm帮助文档制作及C#调用
    OleDb未指定错误
    [学习笔记]工厂方法用于数据库开发
  • 原文地址:https://www.cnblogs.com/weedboy/p/6896478.html
Copyright © 2011-2022 走看看