[LeetCode]406. Queue Reconstruction by Height 解題報告

Suppose you have a random list of people standing in a queue. Each person is described by a pair of integers (h, k), where h is the height of the person and k is the number of people in front of this person who have a height greater than or equal to h. Write an algorithm to reconstruct the queue.

Note:
The number of people is less than 1,100.

Example

Input:
[[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]]

Output:
[[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]]

這一題總的來說個人覺得還是很有難度的。

思路:最開始很容易想到,隨便選一種對整個數列進行排序, 先按k從小到大排,相同的k按照h從小到大排。

方法一,我的思路就是,對每一個People,排在他前面的人比他個字高的人的個數(排序完成以後,第一個人的位置肯定是對的的,因爲假如第一個不對的話,不可能有人比他高, 但是個數又比他少的了,這裏用反證法可以得出)。如果數到他的時候,剛剛好,那麼他的位置就不需要改變;如果還沒到他的時候,個數就已經夠了,那麼就再往下數一個比他高的,把他插入到這個人前面就可以了。例如:

排序好以後的數列是:50,70,61,71,52,44

  1. 檢查50,不變
  2. 檢查70,不變
  3. 檢查61,有1個70符合,不變。
  4. 檢查71,有1個70符合,不變。
  5. 檢查52,50符合,70符合,61符合,61已經是第3個了,所以把52插入到61前面,當前序列是50,70,52,61,71,44。(這個地方需要注意的是,將52插入到61前面,並不影響61的相對位置,因爲只會把一個矮的插到高的前面,對高的沒有影響,不會把高的插到矮的前面)
上面的思路結果正確,但是提交以後超時,它的複雜度大概是nlogn+n^2=n^2

LinkedList<People> listPeople = new LinkedList<People>();

	public int[][] reconstructQueue(int[][] people) {
		for (int[] is : people) {
			listPeople.add(new People(is[0], is[1]));
		}
		Collections.sort(listPeople);
		for (int i = 0; i < listPeople.size(); i++) {
			int nBigger = 0;
			People pCurrent = listPeople.get(i);
			for (int j = 0; j < i; j++) {
				if (listPeople.get(j).nH >= pCurrent.nH) {
					nBigger++;
				}
				if (nBigger > pCurrent.nK) {
					listPeople.remove(i);
					listPeople.add(j, pCurrent);
					break;
				}
			}
		}
				for (int i = 0; i < people.length; i++) {
			people[i][0]=listPeople.get(i).nH;
			people[i][1]=listPeople.get(i).nK;
		}
		return people;
	}



方法二,看了網友的解答以後,發現其實後面不需要對每個進行排序,利用上面提到了的,排好序的第一個位置肯定是固定的,因此,每次取出第一個位置,再排序,直到所有的都取出來。另外,在取出第一個的時候,需要對後面的進行遍歷,如果存在比當前高的人,那麼就對當前的數量減一即可,這樣才能保證每次排序的正確性。例如:第一個取出的50,那麼當遍歷到52時,需要對2減一,在下一次排序的時候,排的就是51。代碼如下:
public class Solution {
    List<int[]> listPeople = new LinkedList<int[]>();

	public int[][] reconstructQueue(int[][] people) {
		for (int[] p : people) {
			listPeople.add(new int[] { p[0], p[1], p[1] });
		}
		int[][] nArrAns = new int[people.length][];

		int nIndex = 0;
		while (listPeople.size() > 0) {
			Collections.sort(listPeople, new Comparator<int[]>() {
				public int compare(int[] a, int[] b) {
					if (a[1] == b[1]) {
						return a[0] - b[0];
					}
					return a[1] - b[1];
				}
			});
			nArrAns[nIndex] = new int[] { listPeople.get(0)[0], listPeople.get(0)[2] };
			listPeople.remove(0);
			for (int[] nCurPeople : listPeople) {
				if (nCurPeople[0] <= nArrAns[nIndex][0]) {
					nCurPeople[1] -= 1;
				}
			}
			nIndex++;
		}
		return nArrAns;
	}
}


提交以後,第二種方法在所有方法中排比較後的位置,因此應該還有速度更快的方法。

事實上,第二種方法的複雜度也相當高,大致是nnlogn+n=n^2logn.
看起來這種方法的複雜度應該比第一個還高,但是我們計算一下具體的係數。
方法一:nlogn+(1+n)n/2=n(n/2+logn+1/2)
方法二:1log1+2log2+...+nlogn+(1+n)n/2
前面的數列無法求和,但是可以用斯特林公式逼緊,但是方法二的複雜度明顯大於方法一。

總的來說,第一種方法理論複雜度比第二種方法低,第二種方法思考起來更加直觀。
我猜想,之所以第一種方法超時,有兩個原因:1.可能是因爲複雜的插入操作造成的,方法二隻有自減操作,這和LeetCode測試用例也有關,可能存在一些非常極端的例子;2.構造的People類有些複雜,new對象,不如直接使用int數組快。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章