这道算法题描述的是:假设有打乱顺序的一群人站成一个队列,每个人由一个整数对(h,k)表示,其中h是这个人的身高,k是排在这个人前面且身高大于或等于h的人数。我们需要根据这些信息重建这个队列。
举个实际例子,假设输入是[[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]],那么正确的输出应该是[[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]]。这个结果满足每个人的k值要求:比如[5,2]前面有两个身高≥5的人([5,0]和[7,0]),[4,4]前面有四个身高≥4的人。
这个问题在现实中有很多应用场景,比如:
看到这个问题,我首先想到的是如何确定每个人的位置。最直观的暴力解法是尝试所有排列组合,然后检查每个排列是否满足条件。但这样时间复杂度是O(n!),显然不可行。
然后我思考能否先排序再调整。一个关键观察是:身高较高的人的位置相对容易确定,因为他们的k值只受更高的人影响。这提示我们可以先处理高个子。
经过几次尝试后,我发现以下规律:
为什么这样可行?因为:
基于以上分析,我们可以:
这比暴力解法高效多了。Python中可以使用list的insert操作,虽然每次insert是O(n),但实际应用中还是可以接受的。
首先我们需要对原始数组进行排序。排序规则是:
Python代码实现:
python复制people.sort(key=lambda x: (-x[0], x[1]))
例如输入[[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]]排序后变为:
[[7,0],[7,1],[6,1],[5,0],[5,2],[4,4]]
然后我们初始化一个空列表,按顺序将每个人插入到其k索引的位置:
python复制result = []
for p in people:
result.insert(p[1], p)
让我们一步步看这个过程:
python复制def reconstructQueue(people):
# 先按h降序,k升序排序
people.sort(key=lambda x: (-x[0], x[1]))
result = []
for p in people:
result.insert(p[1], p)
return result
为什么这个算法是正确的?我们可以从几个方面来理解:
高个子优先:高个子插入时,队列中还没有更矮的人,所以他们的k值只受已经插入的更高的人影响。而更高的人已经按k值排好,所以直接插入到k位置就能满足条件。
矮个子后处理:当插入矮个子时,所有比他高的人已经排好,他只需要插入到满足前面有k个更高的人的位置即可。由于高个子已经固定,插入位置是唯一确定的。
相同身高处理:对于身高相同的人,按k升序排列。这样先处理k值小的,可以确保后面相同身高的人插入时不会影响前面已经满足的条件。
需要额外的O(n)空间存储结果,不考虑输入输出的话是O(n)
如果使用链表结构,插入操作可以是O(1),但查找插入位置仍然是O(n),所以整体还是O(n²)。在Python中,list的insert操作在中间位置插入需要移动元素,所以实际性能可能不如理论分析。
对于大规模数据,可以考虑更高效的数据结构,但在算法题中这个实现已经足够。
python复制test_cases = [
([], []),
([[7,0]], [[7,0]]),
([[7,0],[7,1]], [[7,0],[7,1]]),
([[6,0],[5,0]], [[5,0],[6,0]]),
([[5,0],[6,0]], [[5,0],[6,0]]),
([[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]],
[[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]])
]
这个问题具有贪心选择性质:局部最优解能导致全局最优解。通过每次处理当前最高的人,并放在正确位置,可以确保后续操作不会破坏已经满足的条件。
如果输入的k值可能有误(比如不可能满足),如何检测?可以在重建后验证每个人的k值是否匹配。
如果除了身高还有其他排序维度(如体重、年龄),如何扩展这个算法?可能需要定义更复杂的排序规则。
对于非常大的n(如百万级),如何优化?可能需要更高效的数据结构或并行处理,但基本思路不变。
这个问题的核心在于发现"高个子先排"这一关键观察点。在实际编程面试中,面试官通常会期待你能够通过举例和逐步分析得出这个结论,而不是直接给出答案。建议多练习类似的排序+插入问题,培养解题直觉。