[LeetCode]406. Queue Reconstruction by Height 解题报告

来源:互联网 发布:html合并单元格编程题 编辑:程序博客网 时间:2024/06/05 10:17

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数组快。

0 0
原创粉丝点击