从序列中选择最左边和最右边点的较短方法是什么?

时间:2017-10-24 19:28:27

标签: c++

我试图从一组点(一个点有x和y坐标)中找到点(名为A,B和C,D)。 A和B是最左边的点,C和D是点集中最右边的点。

此外,A和B是具有相同x-坐标但B具有比A更大的y-坐标的点。 C和D是具有相同x-坐标但D具有比C更大的y-坐标的点。

这是for循环。

// pointSeq is a vector of points
// A,B,C and D are set to pointSeq[0]
for(int i = 1 ; i < pointSeq.size(); i++)
{
    if (pointSeq[i].x <= A.x)
    {
        if(pointSeq[i].x < A.x)
        {
            A = pointSeq[i]; B = pointSeq[i];
        }
        else
        {
            if(pointSeq[i].y > B.y)
            {
                B = pointSeq[i];
            }
            else if(pointSeq[i].y < A.y)
            {
                A = pointSeq[i];
            }
        }
    }
    // from here it is to find the C and D
    else if (pointSeq[i].x >= C.x)
    {
        if(pointSeq[i].x > C.x)
        {
            C = pointSeq[i], D = pointSeq[i];
        }
        else
        {
            if (pointSeq[i].y > D.y)
            {
                D = pointSeq[i];    
            }
            else if (pointSeq[i].y < C.y)
            {
                C = pointSeq[i];
            }
        }   
    }

}

这两个部分是非常相似的过程,所以我想知道我是否可以缩短代码以使其更简单。

我想让问题变得简单。它找到位于每个x-min和x-max位置的四个点。 我可以添加更多的线来找到关于y坐标的点(即,y-min和y max的四个点,在这种情况下x坐标是不同的)。

谢谢。

1 个答案:

答案 0 :(得分:1)

我认为简化你在这里做的一种方法是考虑定义一种比较点的方法。给定两个点(x 1 ,y 1 )和(x 2 ,y 2 ),考虑词典比较这些点,可以通过这个功能完成:

bool lexCompare(Point lhs, Point rhs) {
    return (lhs.x < rhs.x) || (lhs.x == rhs.x && lhs.y < rhs.y);
}

此比较首先查看两点的x坐标。如果它们不同,它会纯粹使用x坐标将点相互排列。否则,如果x坐标相同,则比较会查看y坐标以断开关系。

您正在考虑获取最左边的两个点和最右边的两个点,具体来说,抓住最低y坐标的两个最左边的点和最高y坐标的两个最右边的点。如果你考虑一下,你在这里做的事实上是要求A和B是集合中两个按字典顺序排列的最小点,而C和D则是集合中两个按字典顺序排列的最大点。这样,A具有所有点的最低x坐标,并且对于具有最低x坐标的x的可能选择,具有最低可能的y坐标。然后D具有尽可能高的x坐标,并且在可用选项中,选择具有最高y坐标的那个。

好消息是标准库有大量可用于查找这些值的函数。例如,如果你想要一些简单易用的东西,你可以根据这个指标对点进行排序:

std::sort(pointSeq.begin(), pointSeq.end(),
          [](const Point& lhs, const Point& rhs) {
              return (lhs.x < rhs.x) || (lhs.x == rhs.x && lhs.y < rhs.y);
          });
Point A = pointSeq[0];
Point B = pointSeq[1];
Point C = pointSeq[pointSeq.size() - 2];
Point D = pointSeq[pointSeq.size() - 1];

这在时间O(n log n)中运行,这比你上面的要慢,但是更加紧凑。

另一种选择是使用std::minmax_element来找到最小和最大点(分别为A和D),然后将这些元素拉出来并再做一次std::minmax_element来找到B和C:

auto comparator = [](const Point& lhs, const Point& rhs) {
                       return (lhs.x < rhs.x) || (lhs.x == rhs.x && lhs.y < rhs.y);
                  };
auto aAndD = std::minmax_element(pointSeq.begin(), pointSeq.end(), comparator);
Point A = *aAndD.first;
Point D = *aAndD.second;

/* Move A and D to the end of the sequence. */
if (aAndD.first > aAndD.second) std::swap(aAndD.first, std::aAndD.second);
std::iter_swap(aAndD.second, pointSeq.end() - 1);
std::iter_swap(aAndD.first,  pointSeq.end() - 2);

/* Get B and C. */
auto bAndC = std::minmax_element(pointSeq.begin(), pointSeq.end() - 2);
Point B = *bAndC.first;
Point C = *bAndC.second;

此代码在时间O(n)中运行,并且更紧凑地传达了您尝试做的事情。

或者使用std::nth_element重新排序元素,将前两个和后两个元素放在正确的位置:

auto comparator = [](const Point& lhs, const Point& rhs) {
                       return (lhs.x < rhs.x) || (lhs.x == rhs.x && lhs.y < rhs.y);
                  };
std::nth_element(pointSeq.begin(), pointSeq.begin() + 1, pointSeq.end(), comparator);
std::nth_element(pointSeq.begin() + 2, pointSeq.end() - 2, pointSeq.end(), comparator);

/* Sort the two-element ranges at the beginning and end. */
if (comparator(pointSeq[0], pointSet[1])) {
    std::swap(pointSeq[0], pointSeq[1]);
}
if (comparator(pointSeq[pointSeq.size() - 2], pointSeq[pointSeq.size() - 1]) {
    std::swap(pointSeq[pointSeq.size() - 2], pointSeq[pointSeq.size() - 1]);
}

Point A = pointSeq[0];
Point B = pointSeq[1];
Point C = pointSeq[pointSeq.size() - 2];
Point D = pointSeq[pointSeq.size() - 1];