拼图:找到排队的n个人的顺序(基于他们的高度)

时间:2013-10-04 06:32:52

标签: algorithm data-structures

在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)?

12 个答案:

答案 0 :(得分:4)

存在一个具有O(nlogn)平均复杂度的算法,但是最坏的情况复杂度仍然是O(n²)。

要实现此目的,您可以使用二叉树的变体。这个想法是,在这个树中,每个节点对应一个人,并且每个节点跟踪插入节点时他前面有多少人(这是左子树的大小)。

开始以递减高度顺序迭代人员数组,并从根开始将每个人插入树中。插入如下:

  1. 将此人的frontCount与当前节点(开头的根)值进行比较。
  2. 如果小于它,则将节点向左插入值1.将当前节点的值增加1.
  3. 否则,通过按当前节点的值减少人frontCount来向右下降。这样可以将节点放置在正确的位置。
  4. 在所有节点完成后,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. 迭代数组B并构建不等式链(通过在每次迭代时将项放入正确的位置,这里我们可以使用哈希表进行O(1)查找):
    • b0&gt; B1
    • b0&gt; b1&gt; B2
    • b3&gt; b0&gt; b1&gt; B2
    • b3&gt; b0&gt; b1&gt; b4&gt; B2
    • b3&gt; b0&gt; b5&gt; b1&gt; b4&gt; B2
  2. 对数组A进行排序并将其反转
  3. 初始化输出数组X,从#1遍历链并通过将A中的项放入链中定义的位置来填充数组
  4. 步骤#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)解决方案:

算法:

  1. 如果最矮的人的位置是i,那么身高i-1的人将在他的前面。
  2. 我们先确定矮个子的位置,然后再移到第二矮个子。
  3. 按高度排序人。然后从最短到最高迭代。在每个步骤中,您都需要一种有效的方法来将下一个人放到正确的位置。
  4. 我们可以通过使用段树进一步优化此解决方案。参见此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