leetcode 406- Queue Reconstruction by Height 贪心算法的应用

来源:互联网 发布:mysql的好处 编辑:程序博客网 时间:2024/04/30 15:19

406- Queue Reconstruction by Height
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.

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]]

思路一:由题不难看出,将数组排序后,题目会简单很多。我们可以自定义一个排序准则,将按h递增排序,h相同则按k递减排序。此时我们可以利用贪心算法,寻找结构的贪心性质。我们从排序后的数组来看,利用一个指针遍历数组,假设指针之前的数组都是已经排序完成的,则剩下的第一个pair在未排序中h最小,
因此,我们pair的位置应该是第k个空位置,因为后续空位置都将由比当前pair 的h大的pair 填充,而非空位置都比当前pair的 h小,第k个空位置为安全地选择。 代码如下:

class Solution {public:    vector<pair<int, int>> reconstructQueue(vector<pair<int, int>>& people) {      vector<pair<int, int>> res(people.size(),pair<int,int>(INT_MIN,INT_MIN));      auto cmp = [](const pair<int,int>&lhs,const pair<int,int>& rhs){          return (lhs.first < rhs.first)?1:(lhs.first == rhs.first)?(lhs.second > rhs.second):0;      };      sort(people.begin(),people.end(),cmp);      int size = people.size();      for(auto &p:people){          for(int i = 0,j = -1;i <size;++i){              if(res[i] == pair<int,int>(INT_MIN,INT_MIN))++j;              if(j == p.second){res[i] = p; break;}          }      }      return res;    }

思路二:由题不难看出,将数组排序后,题目会简单很多。我们可以自定义一个排序准则,将按h递减排序,h相同则按k递增排序。排序完成后,遍历整个数组,由于元素按h递减排序,因此每个pair的h都比前面所有元素的h小,因此pair的序号便代表前面h比自己大的pair数目。如果序号大于k,则表示该元素位置不对,应该向前移动,插入到对的位置。由于前面所有pair的h都比当前pair的h 要大,因此应该将当前pair移动到第k个位置。由于前面所有pair的h都比当前pair的h 要大,移动pair不会对前面的pair产生影响,前面所有的pair的位置依然满足队列要求。根据这一思路,可以写出如下代码。

    bool cmp(const pair<int,int>& lhs,const pair<int,int>& rhs){        return (lhs.first > rhs.first)?1:((lhs.first==rhs.first)?(lhs.second < rhs.second):0);    }class Solution {public:    vector<pair<int, int>> reconstructQueue(vector<pair<int, int>>& people) {        sort(people.begin(),people.end(),cmp);       for(int i = 0;i <people.size();++i){            if(people[i].second <= i){                pair<int,int> tmp = people[i];                 people.erase(people.begin() + i);                 people.insert(people.begin()+tmp.second,tmp);            }        }        return people;    }};

代码很简洁,但是运用了erase 和insert 函数,因此每次插入交换都需要O(n)的时间复杂度,算法整体需要O(n2) 的时间复杂度。观察这段代码会发现,这与插入排序的代码十分相似,它们都设置一个指针遍历数组,指针之前的元素都已经排序完成,将指针所指的元素与之前的元素对比,并插入到合适的位置。因此我们可以借鉴插入排序的思想,将核心代码改为:

 for(int i = 0;i <people.size();++i){            if(people[i].second < i){                pair<int,int> tmp = people[i];                int j;                for(j = i - 1;j >= tmp.second;--j){                    people[j + 1] = people[j];                 }                people[j + 1] = tmp;            }

测试运行时间从206ms 提升为 59ms,虽然最终代码最坏情况下依然是O(n2) 时间复杂度,但是插入排序对于接近完成的排序具有很高的效率,因此运行速度提高了接近4倍。

0 0
原创粉丝点击