程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> 判斷凸多邊形並排序算法

判斷凸多邊形並排序算法

編輯:關於C++

在平面直角坐標系中,給定一個點序列,判斷這些點是否能夠構成凸多邊形,

並且按照順時針方向輸出這些點。

其他要求:
1.輸出的起始的為距離原點最近的點,如果多點距離原點相等,取其中任一點即可;
2.如果有3個或者以上點在一條直線上,輸出"ERROR";

輸入輸出格式要求:
1.輸入為用逗號分隔的10進制整形數字序列的字符串形式,兩兩組成一個坐標點,如:
"0,0,1,0,1,1",代表輸入了P(0,0),P(1,0),P(1,1)三個點;

2.輸出形式同輸入一致;

 

解析:

一、構造順時針多邊形順序算法:

1.先找一個距離原點最近的點A,然後隨便選一個點B,組成直線集合,可以理解成向量L(A,B),即A->B;

2.再依次遍歷剩下的點,向直線集合中插入;

2.1.依次遍歷直線集合;

2.2.當點X在直線(L1,由L1(startPoint,endPoint)左邊時,記錄L1位置i,並從直線集合中移除,同時在i,i+1位置依次插入L2(L1.startPoint,X),L3(X,L1.endPoint),執行2;

2.3.當點X在所有直線的右側時,添加隊尾直線的end節點到X的直線,執行2;

3.全部點遍歷完畢並插入後,添加隊尾直線的end節點到A點的直線;

4.直線集合中的起點順序即為所有點的順時針順序;

 

二、判斷是否是凸多邊形算法

1.在獲取到順時針的直線集合後,依次遍歷直線,如果剩下的點在直線兩側,則說明是非凸多邊形,否則是凸多邊形;

 

三、判斷點是否在線上,還是在左邊,需要根據斜率來計算,計算斜率時,又要注意有些特殊的直線的斜率是不能直接計算的(除數為0的特殊情況)

 

四、源碼解析

1、定義一個點對象,包含x,y坐標,距離原點的距離

 

/*
 * 
* 文 件 名: Point.java * 描 述: <描述> * 修改時間: 2016-4-17 * 
*/ package com.justinsoft.polygon.model; /** * 點 */ public class Point implements Comparable { private final int x; private final int y; /** * 距離(原點)的平方 */ private final Integer distance; public Point(int x, int y) { this.x = x; this.y = y; this.distance = calcDistance(x, y); } /** * 獲取 x * * @return 返回 x */ public int getX() { return x; } /** * 獲取 y * * @return 返回 y */ public int getY() { return y; } /** * 重載方法 * * @return */ @Override public String toString() { return "Point [x=" + x + ", y=" + y + "]"; } /** * 重載方法 * * @param o * @return */ @Override public int compareTo(Point o) { return getDistance().compareTo(o.getDistance()); } /** * 計算距離(距離原點的) * *
     * @param x
     * @param y
     * @return
     * 
*/ private static int calcDistance(int x, int y) { return x * x + y * y; } /** * 獲取 distance * * @return 返回 distance */ private Integer getDistance() { return distance; } }

 

 

2、定義直線對象(向量),包含起點、終點、斜率

 

/*
 * 
* 文 件 名: Line.java * 描 述: <描述> * 修改時間: 2016-4-17 * 
*/ package com.justinsoft.polygon.model; /** *
 * 線
 * 
*/ public class Line { /** * 起點 */ private final Point start; /** * 終點 */ private final Point end; /** * 斜率 */ private final float slopeRate; /** * <默認構造函數> */ public Line(Point start, Point end) { this.start = start; this.end = end; this.slopeRate = calcSlopeRate(start, end); } /** * 點是否在直線上 * * @param point * @return */ public boolean containsPoint(Point point) { // 1.計算斜率 float slopeRate = calcSlopeRate(point, getStart()); // 2.如果斜率為0,需要判斷是否是特殊情況 if (slopeRate == 0) { // 如果是分母相等,說明x坐標相等,則只有當當前點與另一個點的x坐標也相等時才在一條直線上 float delatX = point.getX() - getStart().getX(); if (0 == delatX) { return point.getX() == getEnd().getX(); } else { return point.getY() == getEnd().getY(); } } return slopeRate == getSlopeRate(); } /** * 點是否在直線的左邊(按照線的起點到終點的方向來看點是左邊還是右邊)
*
* 取點的y坐標時,比較線上的x坐標與點的x坐標大小
* k= (y-y1)/(x-x1) x =(y-y1)/k+x1 * *
     * @param point
     * @return
     * 
*/ public boolean hasLeftPoint(Point point) { // 1.判斷斜率的特殊情況 if (0 == getSlopeRate()) { // 1.1如果線是與x軸垂直的直線,則判斷點的x坐標是否更小 if (getEnd().getX() == getStart().getX()) { return point.getX() < getStart().getX(); } } // 2.計算線上的點的x坐標 float xInLine = (point.getY() - getStart().getY()) / getSlopeRate() + getStart().getX(); return point.getX() < xInLine; } /** * 計算斜率 * *
     * @param p1
     * @param p2
     * @return
     * 
*/ private static float calcSlopeRate(Point p1, Point p2) { float delatY = p2.getY() - p1.getY(); float delatX = p2.getX() - p1.getX(); if (0 == delatX) { return 0; } return delatY / delatX; } /** * 獲取 start * * @return 返回 start */ public Point getStart() { return start; } /** * 獲取 end * * @return 返回 end */ public Point getEnd() { return end; } /** * 獲取 slopeRate * * @return 返回 slopeRate */ private float getSlopeRate() { return slopeRate; } /** * 重載方法 * * @return */ @Override public String toString() { return "Line [start=" + start + ", end=" + end + ", slopeRate=" + slopeRate + "]"; } } 3、算法實現類

 

 

package com.justinsoft.polygon;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import com.justinsoft.polygon.model.Line;
import com.justinsoft.polygon.model.Point;

/**
 * 在平面直角坐標系中,給定一個點序列,判斷這些點是否能夠構成凸多邊形,

 * 並且按照順時針方向輸出這些點。

 * 

 * 其他要求:

 * 1.輸出的起始的為距離原點最近的點,如果多點距離原點相等,取其中任一點即可;

 * 2.如果有3個或者以上點在一條直線上,輸出"ERROR";

 * 

 * 輸入輸出格式要求:

 * 1.輸入為用逗號分隔的10進制整形數字序列的字符串形式,兩兩組成一個坐標點,如:

 * "0,0,1,0,1,1",代表輸入了P(0,0),P(1,0),P(1,1)三個點;

 * 2.輸出形式同輸入一致;

 * 

 * 

 */
public class PolygonSort
{
    /**
     * 多邊形最少點數
     */
    private static final int MIN_POINT_SIZE = 3;
    
    /**
     * 點裡面的元素
     */
    private static final int NUM_IN_POINT = 2;
    
    /**
     * 分隔符
     */
    private static final String SPLIT = ",";
    
    /**
     * 錯誤信息
     */
    private static final String ERROR = "ERROR";
    
    public static String findConvexPolygon(String input)
    {
        try
        {
            // 1.獲取所有的點
            List allPoint = getAllPoint(input);
            
            // 總的點數
            int size = allPoint.size();
            
            // 2.判斷是否有一點在其他兩點所在的直線上
            boolean hasPointInLine = hasPointInLine(allPoint);
            if (hasPointInLine)
            {
                return ERROR;
            }
            
            // 3.取距離原點最近的一個點(之一)
            Point minPoint = getFirstPoint(allPoint);
            allPoint.remove(minPoint);
            
            // 4.再從隊列中任意移除一個點
            Point point = allPoint.remove(0);
            
            // 5.組成任意一條直線(從距離原點最小的點開始)
            Line line = new Line(minPoint, point);
            
            List allLine = new ArrayList(size);
            allLine.add(line);
            
            // 6.向已經存在的線中加入點,重新按照順時針連線(根據點在線的位置來進行判斷)
            for (Point leftPoint : allPoint)
            {
                addPointToLine(allLine, leftPoint);
            }
            
            int lastIndex = allLine.size() - 1;
            Line lastLine = allLine.get(lastIndex);
            
            // 7.線還沒有閉環,缺少從最後一個點到起點的直線
            Line tailLine = new Line(lastLine.getEnd(), minPoint);
            allLine.add(tailLine);
            
            // 8.判斷多邊形是否是凸多邊形
            boolean isConvexPolygon = isConvexPolygon(allLine);
            if (!isConvexPolygon)
            {
                return ERROR;
            }
            
            // 8.拼裝輸出結果
            String seqOrder = getSeqOrder(allLine);
            
            return seqOrder;
        }
        catch (Exception e)
        {
            return ERROR;
        }
    }
    
    /**
     *
     * 拼裝輸出結果
     * 
     * @param allLine
     * @return
     * 
*/ private static String getSeqOrder(List allLine) { StringBuilder order = new StringBuilder(); for (Line line : allLine) { order.append(line.getStart().getX()); order.append(SPLIT); order.append(line.getStart().getY()); order.append(SPLIT); } if (order.toString().endsWith(SPLIT)) { order.deleteCharAt(order.length() - 1); } return order.toString(); } /** *
     * 判斷是否是凸多邊形
     * 
     * @param allLine
     * @return
     * 
*/ private static boolean isConvexPolygon(List allLine) { int size = allLine.size(); List allPoint = new ArrayList(size); for (Line line : allLine) { allPoint.add(line.getStart()); } for (int i = 0; i < size; i++) { boolean hasLeftPoint = false; boolean hasRightPoint = false; Line line = allLine.get(i); if (i + 2 < size) { // 獲取線外的剩下的點 List allLeftPoint = getLeftPoint(allPoint, line); for (Point point : allLeftPoint) { if (line.hasLeftPoint(point)) { hasLeftPoint = true; } else { hasRightPoint = true; } // 按照順時針連線後,如果有點在其中某條線的左邊,同時還有點在其右邊,說明是凹多邊形 if (hasLeftPoint && hasRightPoint) { return false; } } } } return true; } /** *
     * 獲取線外的所有點
     * 
     * @param allPoint
     * @param exceptLine
     * @return
     * 
*/ private static List getLeftPoint(List allPoint, Line exceptLine) { List allTempPoint = new ArrayList(allPoint); allTempPoint.remove(exceptLine.getStart()); allTempPoint.remove(exceptLine.getEnd()); return allTempPoint; } /** *
     * 向所有直線中加入點
     * 
     * @param allLine
     * @param point
     * 
*/ private static void addPointToLine(List allLine, Point point) { boolean hasLeftPoint = false; int size = allLine.size(); for (int i = 0; i < size; i++) { Line line = allLine.get(i); hasLeftPoint = line.hasLeftPoint(point); if (hasLeftPoint) { allLine.remove(i); Line newLeftLine1 = new Line(line.getStart(), point); Line newLeftLine2 = new Line(point, line.getEnd()); allLine.add(i, newLeftLine2); allLine.add(i, newLeftLine1); break; } } if (!hasLeftPoint) { int lastIndex = size - 1; Line newLine = new Line(allLine.get(lastIndex).getEnd(), point); allLine.add(newLine); } } /** *
     * 獲取所有的點 
     * 
     * @param input
     * @return
     * @throws Exception
     * 
*/ private static List getAllPoint(String input) throws Exception { if (null == input) { throw new Exception(); } List allNum = Arrays.asList(input.split(SPLIT)); int numSize = allNum.size(); int pointSize = numSize / NUM_IN_POINT; // 組成點的元素個數如果不是2的倍數或者點的個數小於3,說明都不能組成多變性 if (0 != numSize % NUM_IN_POINT || pointSize < MIN_POINT_SIZE) { throw new Exception(); } List allPoint = new ArrayList(pointSize); try { for (int i = 0; i < numSize;) { int x = Integer.parseInt(allNum.get(i)); int y = Integer.parseInt(allNum.get(i + 1)); Point point = new Point(x, y); allPoint.add(point); i += 2; } return allPoint; } catch (NumberFormatException e) { throw new Exception(); } } /** * 判斷是否有點在其他點的直線上 * *
     * 算法如下:
     * 1.從集合中的第一個點開始遍歷,並出棧;
     * 2.遍歷的當前點(i)和後面的每個點(序號為j,大小依次為i+1,i+2...)組成一條直線,由於當前點已出棧,j的實際序號為j-1;
     * 3.判斷直線後面的點(序號為i+2,由於當前點已出棧,實際序號為i+1開始),是否在這邊直線上
     * @param allPoint
     * @return
     * 
*/ private static boolean hasPointInLine(List allPoint) { List allTempPoint = new ArrayList(allPoint); Iterator iterator = allTempPoint.iterator(); while (iterator.hasNext()) { Point point = iterator.next(); iterator.remove(); int size = allTempPoint.size(); for (int i = 0; i < size; i++) { Line line = new Line(point, allTempPoint.get(i)); if (i + 1 < size) { List allLeftPoint = allTempPoint.subList(i + 1, size); if (hasPointInLine(line, allLeftPoint)) { return true; } } } } return false; } /** *
     * 直線上是否有該點
     * 
     * @param line
     * @param otherPoint
     * @return
     * 
*/ private static boolean hasPointInLine(Line line, List otherPoint) { for (Point point : otherPoint) { if (line.containsPoint(point)) { return true; } } return false; } /** * 取距離原點最小的點 * *
     * @param allPoint
     * @return
     * 
*/ private static Point getFirstPoint(List allPoint) { List allTempPoint = new ArrayList(allPoint); Collections.sort(allTempPoint); return allTempPoint.get(0); } }
4、單元測試類 /* *
 * 文 件 名:  PolygonSortTest.java
 * 描    述:  <描述>
 * 修改時間:  2016-4-17
 * 
*/ package com.justinsoft.polygon; import static org.junit.Assert.assertTrue; import org.junit.Test; /** *
 * <一句話功能簡述>
 * 
 * 
*/ public class PolygonSortTest { /** * Test method for {@link com.justinsoft.polygon.PolygonSort#findConvexPolygon(java.lang.String)}. */ @Test public void testFindConvexPolygon1() { String input = "1,2,3"; String result = PolygonSort.findConvexPolygon(input); assertTrue("ERROR".equalsIgnoreCase(result)); } /** * Test method for {@link com.justinsoft.polygon.PolygonSort#findConvexPolygon(java.lang.String)}. */ @Test public void testFindConvexPolygon2() { String input = "0,0,1,1,0,1"; String result = PolygonSort.findConvexPolygon(input); assertTrue("0,0,0,1,1,1".equalsIgnoreCase(result)); } /** * Test method for {@link com.justinsoft.polygon.PolygonSort#findConvexPolygon(java.lang.String)}. */ @Test public void testFindConvexPolygon3() { String input = "0,0,1,1,0,1,1,0"; String result = PolygonSort.findConvexPolygon(input); assertTrue("0,0,0,1,1,1,1,0".equalsIgnoreCase(result)); } /** * Test method for {@link com.justinsoft.polygon.PolygonSort#findConvexPolygon(java.lang.String)}. */ @Test public void testFindConvexPolygon4() { String input = "0,0,1,1,0,3,3,0"; String result = PolygonSort.findConvexPolygon(input); assertTrue("ERROR".equalsIgnoreCase(result)); } }
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved