Java演算法面試題(005) 求n邊形周長的k等分點座標(今日頭條)

劉近光發表於2017-12-11

題目

本題來自今天頭條的筆試:

有一個n邊形(P0, P1, ..., Pn), 每一條邊皆為垂直或水平線段。現給定數值k,以P0為起點將n邊形的周長分為k段,每段的長度相等,請列印出k等分點的座標(T0, T1, ..., Tk)的座標。

分析

首先,假設n邊形在二維平面的上,每個頂點具有X座標和Y座標。

其次,n邊形的邊具有特殊的性質,每條邊皆為垂直或水平線段。因此,如果我們計算線段長度時,可以通過線段端點的X座標或Y座標相減得到。

最後,由於需要計算n邊形周長和等分點座標,這需要計算兩個相鄰點的距離,考慮到這個特性,我們可以採用線性結構來進行儲存。

接下來我們分析一下具體實現。

Java實現

首先我們來實現表示n邊形頂點座標的類:

public class Coordinate {
	private double x, y;

	public Coordinate(double x, double y) {
		super();
		this.x = x;
		this.y = y;
	}

	public double getX() {
		return x;
	}

	public void setX(double x) {
		this.x = x;
	}

	public double getY() {
		return y;
	}

	public void setY(double y) {
		this.y = y;
	}
	
	@Override
	public String toString() {
		return "x = " + x + ", y=" + y;
	}
}
下面是n邊形的實現類:

public class Polygon {
	private Coordinate[] coArray = null;

	public Polygon(Coordinate[] array) {
		coArray = array;
	}

	/**
	 * 計算多邊形邊長
	 * 
	 * @return
	 */
	public double getPerimeter() {
		double perimeter = 0;

		for (int i = 0; i < coArray.length; i++) {
			int next = i + 1;

			// 如果最後一個頂點的話,需要計算和起點的距離
			if (i == coArray.length - 1) {
				next = 0;
			}

			// 計算每條邊的邊長
			if (coArray[i].getX() == coArray[next].getX()) {
				perimeter += Math.abs(coArray[i].getY() - coArray[next].getY());
			} else {
				perimeter += Math.abs(coArray[i].getX() - coArray[next].getX());
			}
		}

		return perimeter;
	}

	public Coordinate[] getKDivid(int k) {
		double perimeter = getPerimeter();
		double divLen = perimeter / k;
		double len = divLen;
		Coordinate[] coordinates = new Coordinate[k];
		int index = 0;

		for (int i = 0; i < coArray.length; i++) {
			int next = i + 1;
			// 如果最後一個頂點的話,需要計算和起點的距離
			if (i == coArray.length - 1) {
				next = 0;
			}

			//判斷當前的邊是垂直還是水平方向
			if (coArray[i].getX() == coArray[next].getX()) {
				double distance = Math.abs(coArray[i].getY() - coArray[next].getY());

				if (len < distance) {//當前邊上存在k等分點,而且存在多個的情況
					
					//用來記錄距離當前線段起點距離
					double base = 0;
					while (len <= distance) {
						if (coArray[i].getY() > coArray[next].getY()) {
							coordinates[index] = new Coordinate(coArray[i].getX(), coArray[i].getY() - (len + base));
						} else {
							coordinates[index] = new Coordinate(coArray[i].getX(), coArray[i].getY() + (len + base));
						}
						
						base += len;						
						distance -= len;
						len = divLen;
						index++;
					}
					
					//len記錄了當前邊計算完k等分點後還剩餘多長
					len = divLen - distance;
				} else {
					len -= distance;
				}
			} else { //處理垂直方向的情況
				double distance = Math.abs(coArray[i].getX() - coArray[next].getX());

				if (len < distance) {
					double base = 0;
					while (len <= distance) {
						if (coArray[i].getX() > coArray[next].getX()) {
							coordinates[index] = new Coordinate(coArray[i].getX() - (len + base), coArray[i].getY());
						} else {
							coordinates[index] = new Coordinate(coArray[i].getX() + (len + base), coArray[i].getY());
						}

						base += len;
						index++;
						distance -= len;
						len = divLen;
					}
					len = divLen - distance;
				} else {
					len -= distance;
				}
			}
		}

		return coordinates;
	}
}

實現上有幾個技巧:

1. 由於n邊形採用了線性陣列的儲存結構,在計算周長和k等分點的函式中,處理最後一條邊時,需要計算陣列最後一個節點和第一個節點的距離。我們使用了一個next索引變數來記錄下一個索引的位置,如果是最後一個節點,則下一個節點為第一個節點。

2. 在計算k等分點時,需要區分水平線段還是垂直線段,這個影響到座標的計算;另外需要考慮的時,可能存在一個線段上存在多個等分點的情況。

單元測試

設計一些測試用例來計算K等分點,這塊需要注意的是K等分點計算機浮點數表示可能和你手工計算出來的不同,需要做一下處理。

相關文章