https://leetcode.com/problems/queue-reconstruction-by-height/
X. Sort Time Complexity: O(N^2) Space Complexity: O(N)
http://www.cnblogs.com/grandyang/p/5928417.html
这道题给了我们一个队列,队列中的每个元素是一个pair,分别为身高和前面身高不低于当前身高的人的个数,让我们重新排列队列,使得每个pair的第二个参数都满足题意。首先我们来看一种超级简洁的方法,不得不膜拜想出这种解法的大神。首先我们给队列先排个序,按照身高高的排前面,如果身高相同,则第二个数小的排前面。然后我们新建一个空的数组,遍历之前排好序的数组,然后根据每个元素的第二个数字,将其插入到res数组中对应的位置
https://discuss.leetcode.com/topic/60394/easy-concept-with-python-c-java-solution
https://discuss.leetcode.com/topic/60423/java-o-n-2-greedy-solution
X. - O(N^2)
下面这种解法跟解法一很相似,只不过没有使用额外空间,而是直接把位置不对的元素从原数组中删除,直接加入到正确的位置上
X. Using PriorityQueue
X. https://discuss.leetcode.com/topic/60550/o-n-sqrt-n-solution/13
https://discuss.leetcode.com/topic/60437/java-solution-using-priorityqueue-and-linkedlist
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.
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]]
X. Sort Time Complexity: O(N^2) Space Complexity: O(N)
http://www.cnblogs.com/grandyang/p/5928417.html
这道题给了我们一个队列,队列中的每个元素是一个pair,分别为身高和前面身高不低于当前身高的人的个数,让我们重新排列队列,使得每个pair的第二个参数都满足题意。首先我们来看一种超级简洁的方法,不得不膜拜想出这种解法的大神。首先我们给队列先排个序,按照身高高的排前面,如果身高相同,则第二个数小的排前面。然后我们新建一个空的数组,遍历之前排好序的数组,然后根据每个元素的第二个数字,将其插入到res数组中对应的位置
https://discuss.leetcode.com/topic/60394/easy-concept-with-python-c-java-solution
- Pick out tallest group of people and sort them in a subarray (S). Since there's no other groups of people taller than them, therefore each guy's index will be just as same as his k value.
- For 2nd tallest group (and the rest), insert each one of them into (S) by k value. So on and so forth.
E.g.
input: [[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]]
subarray after step 1: [[7,0], [7,1]]
subarray after step 2: [[7,0], [6,1], [7,1]]
input: [[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]]
subarray after step 1: [[7,0], [7,1]]
subarray after step 2: [[7,0], [6,1], [7,1]]
public int[][] reconstructQueue(int[][] people) {
//pick up the tallest guy first
//when insert the next tall guy, just need to insert him into kth position
//repeat until all people are inserted into list
Arrays.sort(people,new Comparator<int[]>(){
@Override
public int compare(int[] o1, int[] o2){
return o1[0]!=o2[0]?-o1[0]+o2[0]:o1[1]-o2[1];
}
});
List<int[]> res = new LinkedList<>();
for(int[] cur : people){
if(cur[1]>=res.size())
res.add(cur);
else
res.add(cur[1],cur);
}
return res.toArray(new int[people.length][]);
}
public int[][] reconstructQueue(int[][] people) {
Arrays.sort(people, (a, b) -> a[0] == b[0] ? a[1] - b[1] : b[0] - a[0]);
List<int[]> list = new LinkedList<>();
for (int[] p : people) {
list.add(p[1], p);
}
return list.toArray(new int[list.size()][]);
}
https://leetcode.com/problems/queue-reconstruction-by-height/discuss/89359/Explanation-of-the-neat-Sort+Insert-solution
Below is my explanation of the following neat solution where we sort people from tall to short (and by increasing k-value) and then just insert them into the queue using their k-value as the queue index:
def reconstructQueue(self, people):
people.sort(key=lambda (h, k): (-h, k))
queue = []
for p in people:
queue.insert(p[1], p)
return queue
I didn't come up with that myself, but here's my own explanation of it, as I haven't seen anybody explain it (and was asked to explain it):
People are only counting (in their k-value) taller or equal-height others standing in front of them. So a smallest person is completely irrelevant for all taller ones. And of all smallest people, the one standing most in the back is even completely irrelevant for everybodyelse. Nobody is counting that person. So we can first arrange everybody else, ignoring that one person. And then just insert that person appropriately. Now note that while this person is irrelevant for everybody else, everybody else is relevant for this person - this person counts exactly everybody in front of them. So their count-value tells you exactly the index they must be standing.
So you can first solve the sub-problem with all but that one person and then just insert that person appropriately. And you can solve that sub-problem the same way, first solving the sub-sub-problem with all but the last-smallest person of the subproblem. And so on. The base case is when you have the sub-...-sub-problem of zero people. You're then inserting the people in the reverse order, i.e., that overall last-smallest person in the very end and thus the first-tallest person in the very beginning. That's what the above solution does, Sorting the people from the first-tallest to the last-smallest, and inserting them one by one as appropriate.
We always choose the current shortest height (so we need to sort input first), and then try to put it into the right position. We simply scan from the left and count how many persons are really >= its own height. Then we put the person into the empty slot.
public int[][] reconstructQueue(int[][] people) {
Arrays.sort(people,new Comparator<int[]>(){
public int compare(int[] p1, int[] p2){
return p1[0]!=p2[0]?Integer.compare(p2[0],p1[0]): Integer.compare(p1[1],p2[1]);
}
});
List<int[]> list = new LinkedList(); // use arraylist or array
for (int[] ppl: people) list.add(ppl[1], ppl);
return list.toArray(new int[people.length][] );
}
https://discuss.leetcode.com/topic/60417/java-solution-using-arrays-sort-and-insert-sorting-ideaX. - O(N^2)
上面那种方法是简洁,但是用到了额外空间,我们来看一种不使用额外空间的解法,这种方法没有没有使用vector自带的insert或者erase函数,而是通过一个变量cnt和k的关系来将元素向前移动到正确位置,移动到方法是通过每次跟前面的元素交换位置,使用题目中给的例子来演示过程:
[[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]]
排序后:
[[7,0], [7,1], [6,1], [5,0], [5,2], [4,4]]
交换顺序:
[[7,0], [6,1], [7,1], [5,0], [5,2], [4,4]]
[[5,0], [7,0], [6,1], [7,1], [5,2], [4,4]]
[[5,0], [7,0], [5,2], [6,1], [7,1], [4,4]]
[[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]]
vector<pair<int, int>> reconstructQueue(vector<pair<int, int>>& people) { sort(people.begin(), people.end(), [](const pair<int, int>& a, const pair<int, int>& b) { return a.first > b.first || (a.first == b.first && a.second < b.second); }); for (int i = 1; i < people.size(); ++i) { int cnt = 0; for (int j = 0; j < i; ++j) { if (cnt == people[i].second) { pair<int, int> t = people[i]; for (int k = i - 1; k >= j; --k) { people[k + 1] = people[k]; } people[j] = t; break; } if (people[j].first >= people[i].first) ++cnt; } } return people; }
vector<pair<int, int>> reconstructQueue(vector<pair<int, int>>& people) { sort(people.begin(), people.end(), [](const pair<int,int> &a, const pair<int, int> &b) { return a.first > b.first || (a.first == b.first && a.second < b.second); }); for (int i = 0; i < people.size(); i++) { auto p = people[i]; if (p.second != i) { people.erase(people.begin() + i); people.insert(people.begin() + p.second, p); } } return people; }
一开始的思路是按照k的大小升序排列,如果k相等就按h升序排列.然后对于每一个元素从头开始到自身位置查找并计数比当前元素大的个数,直到计数比当前元素k大停止,如果此时正好停在当前位置,说明当前元素不需要移动,否则就删除当前元素插入到停止位置之前.这样做的好处是不需要额外申请空间,但是代码稍微麻烦一点.看了一下别人的,发现只需要改变一下排序的规则可以让代码简化很多.就是依然按照h降序排序,但是如果h相等则按照k升序排序.然后开一个新数组,因为数组按照降序排序,所以每次只需要将元素插入到新数组的k的位置即可.两种方法时间复杂度都是O(n*2),但是第二种更容易理解和实现
X. Using PriorityQueue
首先选出k值为0且身高最低的人,记为hi, ki,将其加入结果集。
然后更新队列,若队列中人员的身高≤hi,则令其k值 - 1(需要记录原始的k值)。
循环直到队列为空。
public int[][] reconstructQueue(int[][] people) {
int size = people.length;
LinkedList<int[]> list = new LinkedList<int[]>();
for (int i = 0; i < size; i++) {
list.add(new int[]{people[i][0], people[i][1], 0});
}
int ans[][] = new int[size][];
for (int i = 0; i < size; i++) {
Collections.sort(list, 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];
}
});
int[] head = list.removeFirst();
ans[i] = new int[]{head[0], head[1] + head[2]};
for (int[] p : list) {
if (p[0] <= head[0]) {
p[1] -= 1;
p[2] += 1;
}
}
}
return ans;
}X. https://discuss.leetcode.com/topic/60550/o-n-sqrt-n-solution/13
https://discuss.leetcode.com/topic/60437/java-solution-using-priorityqueue-and-linkedlist
X. https://leetcode.com/problems/queue-reconstruction-by-height/discuss/89367/O(n-sqrt(n))-solution
X. Merge sort
https://leetcode.com/problems/queue-reconstruction-by-height/discuss/143403/O(nlogn)-modified-merge-sort-solution-with-detailed-explanation
https://leetcode.com/problems/queue-reconstruction-by-height/discuss/89342/O(nlogn)-Binary-Index-Tree-C%2B%2B-solution
Instead of just one long list of all people, I break the queue into O(sqrt(n)) blocks of size up to sqrt(n). Then to insert at the desired index, I find the appropriate block, insert the person into that block, and potentially have to break the block into two. Each of those things takes O(sqrt(n)) time.
def reconstructQueue(self, people):
blocks = [[]]
for p in sorted(people, key=lambda (h, t): (-h, t)):
index = p[1]
for i, block in enumerate(blocks):
m = len(block)
if index <= m:
break
index -= m
block.insert(index, p)
if m * m > len(people):
blocks.insert(i + 1, block[m/2:])
del block[m/2:]
return [p for block in blocks for p in block]
X. Merge sort
https://leetcode.com/problems/queue-reconstruction-by-height/discuss/143403/O(nlogn)-modified-merge-sort-solution-with-detailed-explanation
The intuition is the fact that if we start with the people sorted ascending by height, then the
k
value for each person is equal to the number of people that are moved from the right side of a person to the left. You then may realize that you can actually track the number of values moving from right to left during sorting, thus we can modify an existing sorting algorithm (Merge Sort) to take advantage of this fact.Definitions
Inversions: The number of people that must be moved from the right of a given person to left
Merge Sort: Divide and conquer sorting algorithm where sub-parts are sorted separately and then merged. Log-linear time.
Merge: Key step of merge sort where two separately sorted lists are combined in linear time.
Merge Sort: Divide and conquer sorting algorithm where sub-parts are sorted separately and then merged. Log-linear time.
Merge: Key step of merge sort where two separately sorted lists are combined in linear time.
Algorithm
First we sort ascending by height, descending by k if heights are equal. We do this so when we re-sort to the final order, we know how many people of greater than/equal height are moving from right to left. Note that after initial sorting, we know that all people to the right of a person must have height greater than or equal to that person. Initially, the required inversions for each person is the associated
k
value.
The hard part is now to re-order to the final answer. If we use Merge Sort, we can accurately track the number of inversions as we "move" people from right to left.
While performing the Merge step, we will compare people from the left sub-list and right sub-list. To determine which to place first in the final list at each comparison, we examine the number of remaining inversions that still need to occur for each person. We always choose the person with the smaller remaining inversions.
Let's define a few values for brevity:
L
: Current person being compared from the left sub-listR
: Current person being compared from the right sub-listLI
: L
's remaining inversionsRI
: R
's remaining inversions
As metioned before, we must make a choice between
L
and R
at each iteration of merge, but why do we always choose the person with less remaining inversion? We can have 3 scenarios during the comparison; the logic for each choice will be explained below by contradiction i.e. "what if we made the oppposite choice?":LI < RI
: chooseL
. If instead we chose theR
,RI
inversions would also affectL
(because they must move left past both people), but this would exceedLI
, resulting in thek
value ofL
to be off by 1 or more.LI > RI
: chooseR
. If instead we chose to keep theL
on the left, the only way to getLI
people to move left ofL
would be to also move those people pastR
; however these moves would exceedRI
, so thek
value ofR
would be off by 1 or more.LI == RI
: We must choose the shorter person:
3a.L
is shorter thanR
, chooseL
: if instead we chose to placeR
to the left, it would require an additional inversion forL
since a person has moved from right to left, which would result inLI
being decreased to a value lower thanRI
(nowLI
<RI
). However, in order to to moveRI
people pastR
, they must also passL
. This would result in thek
value ofL
being off by 1 or more.
3b.R
is shorter or equal height, chooseR
: if instead we chose to placeL
to the left,RI
would remain equal toLI
. In order to moveLI
people pastL
, they would also move pastR
. However, thek
value ofR
is now off by 1, because in addition to theLI
people,L
(which is now beforeR
) is also of greater than or equal height toR
.
Go code which beats 100%:
func reconstructQueue(people [][]int) [][]int {
// Sort ascending by height, descending by k
sort.Slice(people, func(i,j int) bool {
if people[i][0] == people[j][0] {
// Descending by k
return people[i][1] > people[j][1]
}
// Ascending by height
return people[i][0] < people[j][0]
})
// Make an array allowing tracking current number of inversions without
// modifying original input values (because they need to be preserved for
// the return)
invs := make([]int, len(people))
for i, person := range people {
invs[i] = person[1]
}
mergeSort(people, invs, 0, len(people)-1)
return people
}
func mergeSort(people [][]int, invs []int, left, right int) {
if left >= right {
return
}
middle := (left + right) / 2
mergeSort(people, invs, left, middle)
mergeSort(people, invs, middle+1, right)
merge(people, invs, left, middle, right)
}
func merge(people [][]int, invs []int, left, middle, right int) {
inversions := 0
li, ri := left, middle+1
i := 0
tmp := make([][]int, right-left+1)
tmpInv := make([]int, right-left+1)
for li <= middle && ri <= right {
if invs[li]-inversions < invs[ri] {
tmp[i], tmpInv[i] = people[li], invs[li]-inversions
li++
} else if invs[li]-inversions > invs[ri] {
inversions++
tmp[i], tmpInv[i] = people[ri], invs[ri]
ri++
} else {
if people[li][0] < people[ri][0] {
tmp[i], tmpInv[i] = people[li], invs[li]-inversions
li++
} else {
inversions++
tmp[i], tmpInv[i] = people[ri], invs[ri]
ri++
}
}
i++
}
for li <= middle {
tmp[i], tmpInv[i] = people[li], invs[li]-inversions
li++
i++
}
for ri <= right {
tmp[i], tmpInv[i] = people[ri], invs[ri]
ri++
i++
}
// Copy in tmp array
for ti, person := range tmp {
people[left+ti], invs[left+ti] = person, tmpInv[ti]
}
}
X. BIThttps://leetcode.com/problems/queue-reconstruction-by-height/discuss/89342/O(nlogn)-Binary-Index-Tree-C%2B%2B-solution
1. Sort by height, if height is equal, sort by second item
2. Naive Algorithm:
for each item in seq:
find the new insert position and assign it to the item
3. Case analysis:
[4,4], [5,0], [5,2], [6,1], [7,0], [7,1], total 6 sorted items
that means we must fill 6 blankets. _ _ _ _ _ _
(1)[4,4] means there are 4 items before it, since no other items less than it, so it must be at the 5th pos.
(2)[5,0] means there are 0 items before it, so it must be at the first pos.
......
(6)same as before
visualize process
-----------------
_ _ _ _ _ _
_ _ _ _ [4,4] _
[5,0] _ _ _ [4,4] _
[5,0] _ [5,2] _ [4,4] _
[5,0] _ [5,2] [6,1] [4,4] _
[5,0] [7,0] [5,2] [6,1] [4,4] _
[5,0] [7,0] [5,2] [6,1] [4,4] [7,1]
4. Improved Algorithm
for each item in seq:
pos = find Kth avaliable position in newArray // K = item.second
newArray[pos] = item
used[pos] = true
Implement find_Kth() method can be done in O(n), thus total complexity is O(n^2)
5. Implement find_Kth() method in O(lgN*lgN) or O(lgN) use BIT
(1)trick #1: to convert the former "fill blanket" to "range sum"
(2)trick #2: if height[i] == height[i+1], we must delay the "convert" operation as below described
visualize process
-----------------
1 1 1 1 1 1 // first initialize all position with 1
1 1 1 1 0 1 // find [4,4] pos by calling find_Kth(4+1), pos = 5, and convert 1 to 0
1 1 1 1 0 1 // find [5,0] pos by calling find_Kth(0+1), pos = 1, do not convert 1 to 0
0 1 0 1 0 1 // find [5,2] pos by calling find_Kth(2+1), pos = 3, and convert 1 to 0
0 1 0 0 0 1 // find [6,1] pos by calling find_Kth(1+1), pos = 4, and convert 1 to 0
0 1 0 0 0 1 // find [7,0] pos by calling find_Kth(0+1), pos = 2, do not convert 1 to 0
0 0 0 0 0 0 // find [7,1] pos by calling find_Kth(1+1), pos = 6, convert 1 to 0
Dirty Code Below......
Hope someone can post a nice and clean code.....
Hope someone can post a nice and clean code.....
class Solution {
public:
typedef pair<int,int> Node;
vector<int> c;
int n;
vector<pair<int, int>> reconstructQueue(vector<pair<int, int>>& people) {
int len = people.size();
vector<Node> ans(len);
vector<int> tmp(len+2,0);
c = tmp;
n = len;
//initialize
for(int i = 1; i <= n; i++)update(i,1);
sort(people.begin(), people.end());
int pre = -1;
vector<int> preNum;
for(int i = 0; i < len; i++)
{
//amotized O(1) operation
if(people[i].first != pre)
{
for(int j = 0; j < preNum.size(); j++)update(preNum[j],-1);
preNum.clear();
}
int num = findKth(people[i].second+1);
ans[num-1] = people[i];
preNum.push_back(num);
pre = people[i].first;
}
return ans;
}
//Binary Index Tree update
void update(int idx, int val)
{
while(idx <= n)
{
c[idx] += val;
idx += idx & -idx;
}
}
//Binary Index Tree getSum [1, idx]
int getsum(int idx)
{
int sum = 0;
while(idx > 0)
{
sum += c[idx];
idx -= idx & -idx;
}
return sum;
}
//find-Kth position, Here I use Binary-search, So complexity is O(lgN*lgN)
int findKth(int k)
{
int l = 1, r = n, mid;
while(l <= r)
{
mid = (l + r) >> 1;
if(getsum(mid) >= k)r = mid - 1;
else l = mid + 1;
}
return l;
}
bool static cmp(Node a, Node b)
{
if(a.first == b.first)return a.second < b.second;
return a.first < b.first;
}
};