14

在 Careercup.com 上看到这个问题:

给定 n 个人站在一条直线上的高度,以及与每个人 (p) 相对应的数字列表,给出比 p 高并站在 p 前面的人数。例如,

高度:5 3 2 6 1 4

正面:0 1 2 0 3 2

表示实际实际订单为:5 3 2 1 6 4

该问题获得了 Heights 和 InFronts 的两个列表,并且应该生成排队的订单。

我的解决方案:

可以通过首先按降序对列表进行排序来解决。显然,要进行排序,我们需要定义一个对象 Person(具有 Height 和 InFront 两个属性),然后根据 Persons 的高度对其进行排序。然后,我会使用两个堆栈,一个堆栈和一个临时堆栈来建立订单。

从最高的开始,将其放入主堆栈。如果下一个人的 InFront 值大于堆栈顶部的人,这意味着应该在顶部的人之前添加新的人。因此,我们需要从堆栈中弹出人员,插入新人员,然后将第一步中弹出的人员返回(从临时堆栈返回主堆栈)。我会使用临时堆栈来保持弹出人员的顺序。但是应该弹出多少?由于列表已排序,我们需要准确弹出新人前面的人数,即对应的InFront。

我认为这个解决方案有效。但最坏的情况是 O(n^2) - 当一个人就位时需要弹出所有以前的人。

还有其他解决方案吗?可能在 O(n) 中?

4

12 回答 12

6

O(nlogn) 算法是可能的。

首先假设所有高度都不同。

按高度对人进行排序。然后从最短到最高迭代。在每一步中,您都需要一种有效的方法将下一个人放到正确的位置。请注意,我们已经放置的人并不比当前人高。而我们所追求的人比现在的人高。所以我们必须找到一个位置,使得前面的空位数量等于这个人的 inFronts 值。此任务可以使用称为间隔树的数据结构在 O(logn) 时间内完成。所以一个算法的总时间是O(nlogn)。

该算法在没有联系的情况下效果很好。因为可以安全地假设前面的空位将由较高的人填补。

在可能的情况下,我们需要确保相同身高的人按照他们的位置升序排列。如果我们通过不降低 inFronts 价值来处理人员,就可以实现这一目标。因此,在可能存在联系的情况下,我们还应该在对人员进行排序时考虑 inFronts 值。

如果在某个步骤我们找不到下一个人的职位,那么答案就是“不可能满足问题约束”。

于 2013-10-04T07:10:43.887 回答
4

存在一种平均复杂度为 O(nlogn) 的算法,但最坏情况下的复杂度仍然为 O(n²)。

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

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

  1. frontCount人的值与当前节点的(开头的根)值进行比较。
  2. 如果它小于它,则将值为 1 的节点插入左侧。将当前节点的值增加 1。
  3. frontCount否则,通过将人的值减少当前节点的值来向右下降。这使节点能够放置在正确的位置。

在所有节点完成后,中序遍历给出正确的人员顺序。

让代码自己说话:

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);
}
于 2015-01-04T18:12:15.830 回答
3

我认为一种方法可以是以下。尽管目前似乎又是 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--

第三个元素应位于位置 1。因此最终数组变为:
-321--

第 4 个元素应位于位置 2。这是空元素中的位置。因此最终数组变为:
-321-4

第 5 个元素应位于位置 0。因此最终数组变为:
5321-4

第 6 个元素应位于位置 0。因此最终数组变为:
532164

于 2013-10-04T06:53:09.530 回答
2

我认为上面指出的方法是正确的。但是,上述解决方案中缺少一个关键部分。
Infronts 是当前人之前的更高候选人的数量。因此,在根据身高(升序)对人员进行排序后,将人员 3 放置在 infront=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;
}
于 2016-03-20T22:31:26.727 回答
0

我为此使用 LinkedList。按升序对 tallCount[] 进行排序,并相应地重新定位 heights[] 中的项目。这也能够处理重复的元素。

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 + "]";
    }

}

}

于 2014-08-01T21:37:56.643 回答
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 > b1
    • b0 > b1 > b2
    • b3 > b0 > b1 > b2
    • b3 > b0 > b1 > b4 > b2
    • b3 > b0 > b5 > b1 > b4 > b2
  2. 对数组 A 进行排序并将其反转
  3. 初始化输出数组 X,从 #1 开始遍历链,并通过将 A 中的项目放置到链中定义的位置来填充数组 X

第 1 步和第 3 步是 O(N),第 2 步是最昂贵的 O(N*logN)。并且显然不需要反转排序数组 A(在步骤 #2 中)。

于 2015-02-03T08:19:34.230 回答
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;
        }
    }
于 2015-07-12T16:39:29.407 回答
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
于 2021-05-11T17:13:56.877 回答
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
于 2018-11-03T22:47:59.787 回答
0

Java 中的简单 O(n^2) 解决方案:

算法:

  1. 如果最矮的人的位置是 i,那么 i-1 个更高的人会在他的前面。
  2. 我们固定最矮的人的位置,然后移动到第二个最矮的人。
  3. 按高度对人进行排序。然后从最短到最高迭代。在每一步中,您都需要一种有效的方法将下一个人放到正确的位置。
  4. 我们可以通过使用段树来进一步优化这个解决方案。请参阅此链接

    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;
        }
    }
    
于 2019-03-16T07:35:58.080 回答
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;
        }
    }
}
于 2015-11-05T10:52:36.743 回答
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();

    }

}
于 2016-12-20T22:30:15.707 回答