leetcode解题系列(九)
1122、数组的相对排序
题目描述:
最简单想到的方法自然是:
用一个指针pointer指向arr2,然后依次在arr1中搜索指针指向的元素个数,再将特定数目的元素加入res数组,最后返回res即可,代码如下:
class Solution {
public:
vector<int> relativeSortArray(vector<int>& arr1, vector<int>& arr2) {
vector<int> res;
vector<int> add;
int n=0;
int m=0;
while(n<arr2.size()){
for(int i=count(arr1.begin(),arr1.end(),arr2[n]);i>0;i--){
res.push_back(arr2[n]);
m++;
}
n++;
}
n=0;
while(n<arr1.size()){
if(!count(arr2.begin(),arr2.end(),arr1[n])){
res.push_back(arr1[n]);
}
n++;
}
sort(res.begin()+m,res.end());
return res;
}
};
感觉自己对于vector和指针的知识还是太欠缺了,在这里补一下吧:
C++中vector的用法:
看到了一堆平时没注意但又经常出现的C++概念,今天有时间就好好看一下:
迭代器:
用迭代器(iterator)可以访问顺序容器和关联容器中的元素;
迭代器是一个变量 ,相当于容器和操纵容器的算法之间的中介。迭代器可以指向容器中的某个元素,通过迭代器就可以读写它指向的元素。从这一点上看,迭代器和指针类似。
四种类型:正向、反向、常量正向、常量反向。
其中正向和反向iterator的区别在于进行++运算时,正向迭代器指向后一个元素,而反向迭代器指向前一个元素。常量和非常量的区别在于常量可以修改迭代器指向的元素;
创建四种类型:
vector<int>::iterator ite; //容器类名::iterator 迭代器名;
vector<int>::reverse_iterator ite;//容器类名::reverse_iterator 迭代器名;
vector<int>::const_iterator ite;//容器类名::const_iterator 迭代器名;
vector<int>::const_reverse_iterator ite;//容器类名::const_reverse_iterator 迭代器名;
然后就是写代码用iterator遍历容器中的每一个元素了:
#include<iostream>
#include<vector>
using namespace std;
int main() {
vector<int> v;
for (int n = 0; n < 5; n++) {
v.push_back(n);//向空容器中添加元素
}
vector<int>::iterator i;//正向迭代器
for (i = v.begin(); i != v.end(); i++) {
cout << *i << " ";
*i *= 2;//修改容器中的内容
}
cout << endl;
for (vector<int>::reverse_iterator j = v.rbegin(); j != v.rend(); j++) {
cout << *j << " ";//用反向迭代器遍历
}
return 0;
}
得到的结果是:
需要注意的点:
- v.begin()返回的是一个指向v第一个元素的迭代器;v.end()指向的是一个指向v最后一个元素再之后一个元素的迭代器;所以遍历时的终止条件是(i<v.end())
- v.rbegin()返回的是指向v最后一个元素的迭代器;而v.rend()指向v第一个元素再之前的一个地址,故用reverse_iterator用的是rbegin()和rend();
- 这里使用的是非常量迭代器,故可以修改迭代器指向的元素;
- ++i和i++是一个意思,但是这里的++i会更快:后置形式需要生成一个局部对象tmp,所以会比前置形式慢,如果循环次数较多,建议养成写++i的习惯。
注意:容器适配器stack(栈)、queue(队列)、priority_queue(优先队列)没有迭代器,但是有成员函数可以完成特定功能。
迭代器类型(按功能强弱区分)
主要介绍三种:
迭代器种类 | 功能描述 |
---|---|
正向迭代器: | 功能最弱,只能进行++的操作和*取内容的操作;两个正向迭代器可以互相赋值和比较(==,!=) |
双向迭代器: | 正向迭代器的功能都有,并且有–的操作,可以改变访问的方向 |
随机访问迭代器: | 包含前两种迭代器的功能,且可以随机访问,往前移动n个单位或往后移动n个,可以进行比较(序号的比较),或者相减(序号的差) |
不同容器的迭代器类型:
从上表可以看出,vector容器的迭代器是随机访问类型的,所以功能是比较强大的:
可以直接用v[i]去访问vector内元素;
可以用v.size()得到元素个数;
可以用i<v.end();
而list是双向型的,就不支持下标访问、size、<的运算;
还看到了一种C++泛型编程的方式:函数模板;具体的用法如下所示:(由网上程序改编)
#include<iostream>
using namespace std;
template <typename T>
T max(T a, T b, T c) {
if (b > a)a = b;
if (c > a)a = c;
return a;
}
int main() {
int i1 = 8, i2 = 10, i3 = 2,i;
double d1 = 34.3, d2 = 23.45, d3 = 12.23, d ;
long g1 = 23232, g2 = 526374, g3 = 23423,g;
i = max(i1, i2, i3);
d = max(d1, d2, d3);
g = max(g1, g2, g3);
cout << "i=" << i << endl;
cout << "d=" << d << endl;
cout << "g=" << g << endl;
return 0;
}
输出结果就不用写了;
从这个例子可以看出主要的作用就是同样功能的一个函数用于不同的数据类型;
迭代器中也有三个函数模板:
1、advance(p, n):使迭代器 p 向前或向后移动 n 个元素。
2、distance(p, q):计算两个迭代器之间的距离,即迭代器 p 经过多少次 + + 操作后和迭代器 q 相等。如果调用时 p 已经指向 q 的后面,则这个函数会陷入死循环。
3、iter_swap(p, q):用于交换两个迭代器 p、q 指向的值。
使用他们需要#include
STL:
standrard template library标准模板库
功能强大的C++模板库,提供了提供了通用的模板类和函数,这些模板类和函数可以实现多种流行和常用的算法和数据结构,如向量、链表、队列、栈。
三个组件:容器(container)、迭代器(iterator)、算法(algorithm)
知道这些就差不多了!
类模板:
跟函数模板相对应的:
人们需要编写多个形式和功能都相似的函数,因此有了函数模板来减少重复劳动;人们也需要编写多个形式和功能都相似的类,于是 C++ 引人了类模板的概念,编译器从类模板可以自动生成多个类,避免了程序员的重复劳动。
示例代码:(根据网上代码改编)
#include<iostream>
#include<string>
using namespace std;
template<class T1,class T2>//实际就是告诉你T1,T2可以指代所有数据类型
class Pair {
public:
T1 key;
T2 value;
Pair(T1 k, T2 v) :key(k), value(v) {};//配对函数
bool operator <(const Pair<T1, T2>& p)const;//成员函数的声明
};
template<class T1,class T2>
bool Pair<T1, T2>::operator <(const Pair<T1, T2>& p)const {//成员函数的定义
return key < p.key;
}
int main() {
Pair<string, int> student("Tom", 19);//实例化一个类
Pair<string, int> student1("Peter", 20);
cout << student.key << " " << student.value<<endl;
if (student1 < student)cout << student.key;//比较两者的大小
else cout << student1.key;
return 0;
}
基本看懂这个例子就可以知道类模板是干什么的了。
言归正传,回到这道题本身;leetcode官方给出了两个解答:
- 哈希表的方法,然后自定义排序的标准,重写sort中的rank:
class Solution {
public:
vector<int> relativeSortArray(vector<int>& arr1, vector<int>& arr2) {
unordered_map<int, int> rank;
for (int i = 0; i < arr2.size(); ++i) {
rank[arr2[i]] = i;
}
sort(arr1.begin(), arr1.end(), [&](int x, int y) {
if (rank.count(x)) {
return rank.count(y) ? rank[x] < rank[y] : true;
}
else {
return rank.count(y) ? false : x < y;
}
});
return arr1;
}
};
sort函数的第三个参数实际上就是一个比较的顺序,比较规则;
下面的主体部分,其实都在说明这个规则;
这个写法好难啊,感觉网上都没啥人用到。。。
- 如果不用count函数,每次都遍历的话,可以直接遍历一次然后计数;然后遍历arr2,依次放入arr1;最后arr2中没有的再遍历加入arr1
代码如下:
class Solution {
public:
vector<int> relativeSortArray(vector<int>& arr1, vector<int>& arr2) {
vector<int> ans;
int cnt[1001] = {0};
for (int e : arr1) ++cnt[e];
for (int e : arr2)
while (0 < cnt[e]--) ans.push_back(e);
for (int e = 0; e < 1001; ++e)
while (0 < cnt[e]--) ans.push_back(e);
return ans;
}
};
这个方法还是不错的,不能太依赖于count函数了