在Careercup.com上看到这个问题:
考虑到排在一条线上的n个人的高度以及与每个人(p)相对应的数字列表,其给出了高于p并且站在p前面的人数。例如,
高度:5 3 2 6 1 4
InFronts:0 1 2 0 3 2
表示实际的实际订单为:5 3 2 1 6 4
问题得到了两个高度和InFronts列表,并且应该生成排在一起的订单。
可以通过首先按降序对列表进行排序来解决。显然,要进行排序,我们需要定义一个对象Person(具有Height和InFront的两个属性),然后根据它们的高度对Persons进行排序。然后,我将使用两个堆栈,一个主堆栈和一个临时堆栈来构建订单。
从最高层开始,将它放在主堆栈中。如果下一个人的InFront值大于堆叠顶部的人,则意味着应该在最顶层的人之前添加新人。因此,我们需要从主堆栈中弹出人员,插入新人员,然后返回在第一步中弹出的人员。我会使用临时堆栈来保持弹出的人的顺序。但应该弹出多少?由于列表是排序的,我们需要准确地弹出新人面前的人数,即相应的InFront。
我认为这个解决方案有效。但最糟糕的情况是O(n ^ 2) - 当一个人到位时需要弹出以前的所有。
还有其他解决方案吗?可能在O(n)?
答案 0 :(得分:4)
存在一个具有O(nlogn)平均复杂度的算法,但是最坏的情况复杂度仍然是O(n²)。
要实现此目的,您可以使用二叉树的变体。这个想法是,在这个树中,每个节点对应一个人,并且每个节点跟踪插入节点时他前面有多少人(这是左子树的大小)。
开始以递减高度顺序迭代人员数组,并从根开始将每个人插入树中。插入如下:
frontCount
与当前节点(开头的根)值进行比较。frontCount
来向右下降。这样可以将节点放置在正确的位置。在所有节点完成后,inorder遍历会给出正确的人员顺序。
让代码说明一切:
public static void arrange(int[] heights, int[] frontCounts) {
Person[] persons = new Person[heights.length];
for (int i = 0; i < persons.length; i++)
persons[i] = new Person(heights[i], frontCounts[i]);
Arrays.sort(persons, (p1, p2) -> {
return Integer.compare(p2.height, p1.height);
});
Node root = new Node(persons[0]);
for (int i = 1; i < persons.length; i++) {
insert(root, persons[i]);
}
inOrderPrint(root);
}
private static void insert(Node root, Person p) {
insert(root, p, p.frontCount);
}
private static void insert(Node root, Person p, int value) {
if (value < root.value) { // should insert to the left
if (root.left == null) {
root.left = new Node(p);
} else {
insert(root.left, p, value);
}
root.value++; // Increase the current node value while descending left!
} else { // insert to the right
if (root.right == null) {
root.right = new Node(p);
} else {
insert(root.right, p, value - root.value);
}
}
}
private static void inOrderPrint(Node root) {
if (root == null)
return;
inOrderPrint(root.left);
System.out.print(root.person.height);
inOrderPrint(root.right);
}
private static class Node {
Node left, right;
int value;
public final Person person;
public Node(Person person) {
this.value = 1;
this.person = person;
}
}
private static class Person {
public final int height;
public final int frontCount;
Person(int height, int frontCount) {
this.height = height;
this.frontCount = frontCount;
}
}
public static void main(String[] args) {
int[] heights = {5, 3, 2, 6, 1, 4};
int[] frontCounts = {0, 1, 2, 0, 3, 2};
arrange(heights, frontCounts);
}
答案 1 :(得分:3)
O(nlogn)算法是可能的。
首先假设所有高度都不同。
按高度对人进行排序。然后从最短到最高迭代。在每一步中,您都需要一种有效的方法将下一个人放在正确的位置。请注意,我们已经放置的人并不比现在的人高。而我们所追求的人比现在的人高。所以我们必须找到一个地方,使得前面的空位数等于这个人的inFronts值。可以使用O(logn)时间内名为interval tree的数据结构来完成此任务。所以算法的总时间是O(nlogn)。
该算法在没有关系的情况下运行良好。因为可以安全地假设前面的空位将被更高的人填补。
如果可能有关系,我们需要确保相同高度的人按其位置的递增顺序放置。如果我们将通过非递减inFronts值处理人员,就可以实现这一目标。因此,如果存在可能的联系,我们还应该在对人员进行排序时考虑inFronts值。
如果在某个步骤我们找不到下一个人的位置,那么答案是“它不可能满足问题约束”。
答案 2 :(得分:2)
我认为上述方法是正确的。然而,上述解决方案中缺少的一个关键部分是 Infronts是现任人之前较高的候选人数。因此,在根据身高(升序)对人进行排序后,当人3位于面前= 2时,如果人1和2分别位于0,1位,那么你需要打折他们的位置并将位置3放在第4位, IE 2较高的候选人将获得2,3位。
正如一些指示的间隔树是正确的结构。但是,具有可用位置的动态大小的容器将完成这项工作。(下面的代码)
struct Person{
int h, ct;
Person(int ht, int c){
h = ht;
ct = c;
}
};
struct comp{
bool operator()(const Person& lhs, const Person& rhs){
return (lhs.h < rhs.h);
}
};
vector<int> heightOrder(vector<int> &heights, vector<int> &infronts) {
if(heights.size() != infronts.size()){
return {};
}
vector<int> result(infronts.size(), -1);
vector<Person> persons;
vector<int> countSet;
for(int i= 0; i< heights.size(); i++){
persons.emplace_back(Person(heights[i], infronts[i]));
countSet.emplace_back(i);
}
sort(persons.begin(), persons.end(), comp());
for(size_t i=0; i<persons.size(); i++){
Person p = persons[i];
if(countSet.size() > p.ct){
int curr = countSet[p.ct];
//cout << "the index to place height=" << p.h << " , is at pos=" << curr << endl;
result[curr] = p.h;
countSet.erase(countSet.begin() + p.ct);
}
}
return result;
}
答案 3 :(得分:1)
我认为一种方法可以是以下方法。虽然目前似乎又是O(n ^ 2)。
按照高度的升序(在O(nlogn)中)对高度数组和对应的'p'数组进行排序。选择列表中的第一个元素。将该元素放在最终数组中,由p索引给定。
例如在排序后,
H - 1,2,3,4,5,6
p - 3,2,1,2,0,0。
第一个元素应该进入位置3.因此最终的数组变为:
--- 1 -
第二个元素应该进入位置2.因此最终的数组变为:
- 21 -
第3个元素应该进入位置1.因此最终的数组变为:
-321 -
第4个元素应该进入位置2.这是空元素之间的位置。因此最终的数组变为:
-321-4
第5个元素应该进入位置0.因此最终的数组变为:
5321-4
第6个元素应该进入位置0.因此最终的数组变为:
532164
答案 4 :(得分:0)
我正在使用LinkedList。按升序对tallCount []进行排序,并相应地将项目重新定位在高度[]中。这也能够处理重复元素。
public class FindHeightOrder {
public int[] findOrder(final int[] heights, final int[] tallCount) {
if (heights == null || heights.length == 0 || tallCount == null
|| tallCount.length == 0 || tallCount.length != heights.length) {
return null;
}
LinkedList list = new LinkedList();
list.insertAtStart(heights[0]);
for (int i = 1; i < heights.length; i++) {
if (tallCount[i] == 0) {
Link temp = list.getHead();
while (temp != null && temp.getData() <= heights[i]) {
temp = temp.getLink();
}
if (temp != null) {
if (temp.getData() <= heights[i]) {
list.insertAfterElement(temp.getData(), heights[i]);
} else {
list.insertAtStart(heights[i]);
}
} else {
list.insertAtEnd(heights[i]);
}
} else {
Link temp = list.getHead();
int pos = tallCount[i];
while (temp != null
&& (temp.getData() <= heights[i] || pos-- > 0)) {
temp = temp.getLink();
}
if (temp != null) {
if (temp.getData() <= heights[i]) {
list.insertAfterElement(temp.getData(), heights[i]);
} else {
list.insertBeforeElement(temp.getData(), heights[i]);
}
} else {
list.insertAtEnd(heights[i]);
}
}
}
Link fin = list.getHead();
int i = 0;
while (fin != null) {
heights[i++] = fin.getData();
fin = fin.getLink();
}
return heights;
}
public class Link {
private int data;
private Link link;
public Link(int data) {
this.data = data;
}
public int getData() {
return data;
}
public void setData(int data) {
this.data = data;
}
public Link getLink() {
return link;
}
public void setLink(Link link) {
this.link = link;
}
@Override
public String toString() {
return this.data + " -> "
+ (this.link != null ? this.link : "null");
}
}
public class LinkedList {
private Link head;
public Link getHead() {
return head;
}
public void insertAtStart(int data) {
if (head == null) {
head = new Link(data);
head.setLink(null);
} else {
Link link = new Link(data);
link.setLink(head);
head = link;
}
}
public void insertAtEnd(int data) {
if (head != null) {
Link temp = head;
while (temp != null && temp.getLink() != null) {
temp = temp.getLink();
}
temp.setLink(new Link(data));
} else {
head = new Link(data);
}
}
public void insertAfterElement(int after, int data) {
if (head != null) {
Link temp = head;
while (temp != null) {
if (temp.getData() == after) {
Link link = new Link(data);
link.setLink(temp.getLink());
temp.setLink(link);
break;
} else {
temp = temp.getLink();
}
}
}
}
public void insertBeforeElement(int before, int data) {
if (head != null) {
Link current = head;
Link previous = null;
Link ins = new Link(data);
while (current != null) {
if (current.getData() == before) {
ins.setLink(current);
break;
} else {
previous = current;
current = current.getLink();
if (current != null && current.getData() == before) {
previous.setLink(ins);
ins.setLink(current);
break;
}
}
}
}
}
@Override
public String toString() {
return "LinkedList [head=" + this.head + "]";
}
}
}
答案 5 :(得分:0)
由于人们已经纠正了原始输入:
Heights : A[] = { 5 3 2 6 1 4 }
InFronts: B[] = { 0 1 2 0 3 2 }
Output should look like: X[] = { 5 3 1 6 2 4 }
这是接近解决方案的O(N * logN)方法(假设没有联系)。
步骤#1和#3是O(N),步骤#2是最昂贵的O(N * logN)。 显然,不需要反转排序的数组A(在步骤#2中)。
答案 6 :(得分:0)
这是user1990169提供的想法的实现。复杂性为O(N ^ 2)。
public class Solution {
class Person implements Comparator<Person>{
int height;
int infront;
public Person(){
}
public Person(int height, int infront){
this.height = height;
this.infront = infront;
}
public int compare(Person p1, Person p2){
return p1.height - p2.height;
}
}
public ArrayList<Integer> order(ArrayList<Integer> heights, ArrayList<Integer> infronts) {
int n = heights.size();
Person[] people = new Person[n];
for(int i = 0; i < n; i++){
people[i] = new Person(heights.get(i), infronts.get(i));
}
Arrays.sort(people, new Person());
Person[] rst = new Person[n];
for(Person p : people){
int count = 0;
for(int i = 0; i < n ; i++){
if(count == p.infront){
while(rst[i] != null && i < n - 1){
i++;
}
rst[i] = p;
break;
}
if(rst[i] == null) count++;
}
}
ArrayList<Integer> heightrst = new ArrayList<Integer>();
for(int i = 0; i < n; i++){
heightrst.add(rst[i].height);
}
return heightrst;
}
}
答案 7 :(得分:0)
今天解决了这个问题,这就是我想出的:
我们的想法是按降序对高度数组进行排序。有一次,我们有这个排序的数组 - 从这个元素中获取一个元素并将它放在相应索引的结果数组中(我使用的是ArrayList,使用LinkedList会很好):
public class Solution {
public ArrayList<Integer> order(ArrayList<Integer> heights, ArrayList<Integer> infronts) {
Person[] persons = new Person[heights.size()];
ArrayList<Integer> res = new ArrayList<>();
for (int i = 0; i < persons.length; i++) {
persons[i] = new Person(heights.get(i), infronts.get(i));
}
Arrays.sort(persons, (p1, p2) -> {
return Integer.compare(p2.height, p1.height);
});
for (int i = 0; i < persons.length; i++) {
//System.out.println("adding "+persons[i].height+" "+persons[i].count);
res.add(persons[i].count, persons[i].height);
}
return res;
}
private static class Person {
public final int height;
public final int count;
public Person(int h, int c) {
height = h;
count = c;
}
}
}
答案 8 :(得分:0)
我在SPOJ上发现了这种问题。我创建了一个变化很小的二叉树。当插入一个新的高度时,如果前面小于根的前面,那么它会向右移动。
这是C ++实现:
#include<bits/stdc++.h>
using namespace std;
struct TreeNode1
{
int val;
int _front;
TreeNode1* left;
TreeNode1*right;
};
TreeNode1* Add(int x, int v)
{
TreeNode1* p= (TreeNode1*) malloc(sizeof(TreeNode1));
p->left=NULL;
p->right=NULL;
p->val=x;
p->_front=v;
return p;
}
TreeNode1* _insert(TreeNode1* root, int x, int _front)
{
if(root==NULL) return Add(x,_front);
if(root->_front >=_front)
{
root->left=_insert(root->left,x,_front);
root->_front+=1;
}
else
{
root->right=_insert(root->right,x,_front-root->_front);
}
return root;
}
bool comp(pair<int,int> a, pair<int,int> b)
{
return a.first>b.first;
}
void in_order(TreeNode1 * root, vector<int>&v)
{
if(root==NULL) return ;
in_order(root->left,v);
v.push_back(root->val);
in_order(root->right,v);
}
vector<int>soln(vector<int>h, vector<int>in )
{
vector<pair<int , int> >vc;
for(int i=0;i<h.size();i++) vc.push_back( make_pair( h[i],in[i] ) );
sort(vc.begin(),vc.end(),comp);
TreeNode1* root=NULL;
for(int i=0;i<vc.size();i++)
root=_insert(root,vc[i].first,vc[i].second);
vector<int>v;
in_order(root,v);
return v;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int n;
scanf("%d",&n);
vector<int>h;
vector<int>in;
for(int i=0;i<n;i++) {int x;
cin>>x;
h.push_back(x);}
for(int i=0;i<n;i++) {int x; cin>>x;
in.push_back(x);}
vector<int>v=soln(h,in);
for(int i=0;i<n-1;i++) cout<<v[i]<<" ";
cout<<v[n-1]<<endl;
h.clear();
in.clear();
}
}
答案 9 :(得分:0)
这是一个仅使用基本列表函数并处理关系的Python解决方案。
def solution(heights, infronts):
person = list(zip(heights, infronts))
person.sort(key=lambda x: (x[0] == 0, x[1], -x[0]))
output = []
for p in person:
extended_output = output + [p]
extended_output.sort(key=lambda x: (x[0], -x[1]))
output_position = [p for p in extended_output].index(p) + p[1]
output.insert(output_position, p)
for c, p in enumerate(output):
taller_infronts = [infront for infront in output[0:c] if infront[0] >= p[0]]
assert len(taller_infronts) == p[1]
return output
答案 10 :(得分:0)
使用Java的简单O(n ^ 2)解决方案:
算法:
我们可以通过使用段树进一步优化此解决方案。参见此link。
class Person implements Comparable<Person>{
int height;
int pos;
Person(int height, int pos) {
this.height = height;
this.pos = pos;
}
@Override
public int compareTo(Person person) {
return this.height - person.height;
}
}
public class Solution {
public int[] order(int[] heights, int[] positions) {
int n = heights.length;
int[] ans = new int[n];
PriorityQueue<Person> pq = new PriorityQueue<Person>();
for( int i=0; i<n; i++) {
pq.offer(new Person(heights[i], positions[i]) );
}
for(int i=0; i<n; i++) {
Person person = pq.poll();
int vacantTillNow = 0;
int index = 0;
while(index < n) {
if( ans[index] == 0) vacantTillNow++;
if( vacantTillNow > person.pos) break;
index++;
}
ans[index] = person.height;
}
return ans;
}
}
答案 11 :(得分:0)
如果高度没有关系,可以使用段树在 O(nlog n) 中解决这个问题。 请在此链接中查找方法 3,以获得对该方法的清晰解释。
https://www.codingninjas.com/codestudio/problem-details/order-of-people-heights_1170764
下面是我在 python 中使用相同方法的代码
def findEmptySlot(tree, root, left, right, K, result):
tree[root]-=1
if left==right:
return left
if tree[2*root+1] >= K:
return findEmptySlot(tree, 2*root+1, left, (left+right)//2, K, result)
else:
return findEmptySlot(tree, 2*root+2, (left+right)//2+1, right, K-tree[2*root+1], result)
def buildsegtree(tree, pos, start, end):
if start==end:
tree[pos]=1
return tree[pos]
mid=(start+end)//2
left = buildsegtree(tree, 2*pos+1,start, mid)
right = buildsegtree(tree,2*pos+2,mid+1, end)
tree[pos]=left+right
return tree[pos]
class Solution:
# @param A : list of integers
# @param B : list of integers
# @return a list of integers
def order(self, A, B):
n=len(A)
people=[(A[i],B[i]) for i in range(len(A))]
people.sort(key=lambda x: (x[0], x[1]))
result=[0]*n
tree=[0]*(4*n)
buildsegtree(tree,0, 0, n-1)
for i in range(n):
idx=findEmptySlot(tree, 0, 0, n-1, people[i][1]+1, result)
result[idx]=people[i][0]
return result