【LeetCode贪心#08】根据身高重建队列(还是涉及处理两个维度的信息)
根据身高重建队列
假设有打乱顺序的一群人站成一个队列,数组 people 表示队列中一些人的属性(不一定按顺序)。每个 people[i] = [hi, ki] 表示第 i 个人的身高为 hi ,前面 正好 有 ki 个身高大于或等于 hi 的人。
请你重新构造并返回输入数组 people 所表示的队列。返回的队列应该格式化为数组 queue ,其中 queue[j] = [hj, kj] 是队列中第 j 个人的属性(queue[0] 是排在队列前面的人)。
示例 1:
- 输入:people = [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]]
- 输出:[[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]]
- 解释:
- 编号为 0 的人身高为 5 ,没有身高更高或者相同的人排在他前面。
- 编号为 1 的人身高为 7 ,没有身高更高或者相同的人排在他前面。
- 编号为 2 的人身高为 5 ,有 2 个身高更高或者相同的人排在他前面,即编号为 0 和 1 的人。
- 编号为 3 的人身高为 6 ,有 1 个身高更高或者相同的人排在他前面,即编号为 1 的人。
- 编号为 4 的人身高为 4 ,有 4 个身高更高或者相同的人排在他前面,即编号为 0、1、2、3 的人。
- 编号为 5 的人身高为 7 ,有 1 个身高更高或者相同的人排在他前面,即编号为 1 的人。
- 因此 [[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]] 是重新构造后的队列。
示例 2:
- 输入:people = [[6,0],[5,0],[4,0],[3,2],[2,2],[1,4]]
- 输出:[[4,0],[5,0],[2,2],[3,2],[1,4],[6,0]]
提示:
- 1 <= people.length <= 2000
- 0 <= hi <= 10^6
- 0 <= ki < people.length
题目数据确保队列可以被重建
思路
乍一看题目没看懂,感觉需要考虑的要素很多
举个例子:[7,0]
意思就是当前这个人身高是7,前面有0个身高更高或者相同的人
然后题目要求我们对people数组进行排序,并满足对应要求,即上面这个[7,0]在排序后的位置必须满足:当前这个人身高是7,前面有0个身高更高或者相同的人
此时有两个元素需要考虑,即**当前的身高 h 和前面有几个相同身高的人 k **
参考发糖果,涉及两个维度时,需要分别处理
以 示例1 来讲
- 输入:people = [[7,0],[4,4],[7,1],[5,0],[6,1],[5,2]]
- 输出:[[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]]
可以先针对身高进行排序,结果如下:
[[7,0], [7,1], [6,1], [5,0], [5,2],[4,4]]
这里需要自定义sort的排序规则,详见
排序逻辑:显然,我们希望先比较身高h,如果身高相同就比较k
然后使用 k 作为下标,将数组插入到对应位置,模拟过程如下:
定义一个新数组(后续会改用链表)
-
插入[7,0]:[[7,0]]
-
插入[7,1]:[[7,0],[7,1]]
-
插入[6,1]:[[7,0],[6,1],[7,1]]
(身高为6,前面只能有1个大于等于6的,所以它可以插在当前位置,该位置也恰好是1)
-
插入[5,0]:[[5,0],[7,0],[6,1],[7,1]](身高为5,前面只能有0个大于等于5的,只能插在最开始,即0处)
-
插入[5,2]:[[5,0],[7,0],[5,2],[6,1],[7,1]]
-
插入[4,4]:[[5,0],[7,0],[5,2],[6,1],[4,4],[7,1]]
此时完成插入的数组,与 示例1 输出一致
题外话:std::list<vector>
什么时候用std::list<vector<int>>
,什么时候用list<vector<int>>
?
std::list<vector<int>>
和 list<vector<int>>
都是 STL 容器,只是前者在使用时需要加上 std::
命名空间限定符,后者需要引入 using namespace std
或使用 std::list
。它们的本质是相同的,都是双向链表容器,存储的元素类型都是 vector<int>
。
一般情况下,建议使用 list<vector<int>>
,因为 std::
命名空间限定符的使用会增加代码的可读性复杂度。但是如果你的代码中使用了 using namespace std
,那么就可以使用 std::list<vector<int>>
,或者在函数内使用 using std::list
。
需要注意的是,使用链表容器时需要注意在频繁的插入、删除操作时,链表的效率要比 vector 容器高。如果你的应用场景需要频繁进行元素的插入、删除操作,那么链表容器会更适合。但是,如果需要随机访问元素,则应使用 vector 容器。
代码
题外话:容器list
插入和删除
#include <list>
void printList(const list<int>& L) {
for (list<int>::const_iterator it = L.begin(); it != L.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
//插入和删除
void test01(){
list<int> L;
//尾插
L.push_back(30);
//头插
L.push_front(300);
printList(L);
//尾删
L.pop_back();
printList(L);
//头删
L.pop_front();
printList(L);
//插入
list<int>::iterator it = L.begin();
L.insert(++it, 1000);
printList(L);
//删除
it = L.begin();
L.erase(++it);
printList(L);
//移除
L.push_back(10000);
printList(L);
L.remove(10000);
printList(L);
//清空
L.clear();
printList(L);
}
int main() {
test01();
system("pause");
return 0;
}
完整代码
步骤:
1、自定义排序规则cmp
2、对people数组进行排序
3、创建一个list用于存放插入后的数组(初始化迭代器)
4、for循环遍历people数组,获取K作为位置值并保存
5、使用位置值不断修改迭代器it,找到插入位置。然后插入对应的people中的vector
class Solution {
public:
static bool cmp(const vector<int>& a, const vector<int>& b){
//身高从大到小排.如果身高相等,就比较前面有几个相同身高的人(身高相同k小的站前面)
if(a[0] == b[0]) return a[1] < b[1];//比较k值(从小到大)
return a[0] > b[0];//比较身高(从大到小)
}
vector<vector<int>> reconstructQueue(vector<vector<int>>& people) {
sort(people.begin(), people.end(), cmp);//对people进行排序
list<vector<int>> que;//创建一个list用于存放插入后的数组
for(int i = 0; i < people.size(); ++i){
int pos = people[i][1];//获取k值作为位置值
list<vector<int>>::iterator it = que.begin();//初始化迭代器
while(pos--){//使用pos位置变量确认需要插入的位置
it++;//修改迭代器指向的位置
}
que.insert(it, people[i]);
}//因为要返回的是一个vector,所以还要以que的范围来创建一个新的vector
return vector<vector<int>>(que.begin(), que.end());
}
};
题外话:范围构造函数(Range Constructor)
上面代码中,因为题目要求返回类型是vector,所以需要将 que
(STL 容器,list)中的元素转换为一个二维向量 vector<vector<int>>
具体来说,que.begin()
和 que.end()
分别返回指向容器 que
中第一个和最后一个元素的迭代器。这个代码片段使用这两个迭代器作为参数传递给 vector
的构造函数,这样就可以创建一个新的二维向量,其中包含了容器 que
中的所有元素。每个元素都被转换为一个 vector<int>
,并作为二维向量中的一行。
这种创建 vector
的方式叫做范围构造函数(Range Constructor)。范围构造函数允许我们使用一个迭代器范围(例如 begin
和 end
)来创建一个 vector
,从而将另一个容器(或者是一段数组)中的元素复制到新的 vector
中。
使用范围构造函数可以方便地将一个容器或数组中的元素快速转换为一个 vector
对象,并且不需要手动逐个插入元素。