JZ29 最小的k个数
题目描述
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4。
示例 1:
输入:arr = [3,2,1], k = 2
输出:[1,2] 或者 [2,1]
示例 2:
输入:arr = [0,1,2,1], k = 1
输出:[0]
题解:
方法一:排序(需改变输入数组)
直接排序,然后取前k小数据。
class Solution {
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
//1、
vector<int> ret;
if (k==0 || k>arr.size()) return ret;
sort(arr.begin(), arr.end());
for (int i = 0; i < k; ++i) {
//2、
ret.push_back(arr[i]);
}
return ret;
}
};
//注意1、2、两处,
若vector<int> ret,这只是定义,ret是空的,需用ret.push_back()来添加元素
若vector<int> ret(k)或vector<int> ret(k,0),其执行了初始化,ret开始就有K个0,其添加元素需用下标形式,即ret[i]=arr[i],若用ret.push_back()是在0后继续添加。
时间复杂度:O(nlongn),其中 n是数组 arr 的长度。算法的时间复杂度即排序的时间复杂度。
空间复杂度:O(logn),排序所需额外的空间复杂度为O(logn)。
方法二:堆排序
方法一中决定时间复杂度的因素是sort()函数使用的算法
的时间复杂度就是O(nlongn),这在目前的排序算法中已经是最快的了,所以若想更快些,需借助数据结构
来实现。
大根堆(大顶堆
)是由完全二叉树实现的一个数据结构,其重建一次堆的时间是O(logk),k是堆中结点的数量。
大根堆的特点是每个结点的值都大于或等于其左右孩子节点的值。也就是堆顶(根节点)
值最大。
具体步骤:
我们用一个大根堆实时维护数组的前 k小值。
首先
将前 k 个数插入大根堆中(没插入一个数,大根堆就重建一次,重建一次用时O(logk)),
随后
从第 k+1个数开始遍历,如果当前遍历到的数比大根堆的堆顶的数要小,就把堆顶的数弹出,再插入当前遍历到的数。
最后
将大根堆里的数存入数组返回即可。
C++ 语言中的堆(默认是大根堆)由优先队列priority_queue
表示
Java 中提供了现成的 PriorityQueue(默认小根堆),默认是小根堆,实现大根堆需要重写一下比较器。PriorityQueue<Integer> pq = new PriorityQueue<>((v1 , v2) -> v2 - v1);
代码:
class Solution {
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
vector<int> ret;
if (k==0 || k>arr.size()) return ret;
priority_queue<int> pq;
for(int i=0;i<k;i++){
pq.push(arr[i]);
}
for(int i=k;i<arr.size();i++){
if(arr[i]<pq.top()){
pq.pop();
pq.push(arr[i]);
}
}
for(int i=0;i<k;i++){
ret.push_back(pq.top());
pq.pop();
}
return ret;
}
};
Java
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
if(k == 0 || arr.length == 0 || arr.length < k) return new int[0];
PriorityQueue<Integer> pq = new PriorityQueue<>((v1 , v2) -> v2 - v1);
for(int val : arr){
if(pq.size() < k){
pq.offer(val);
}else{
if(val < pq.peek()){
pq.poll();
pq.offer(val);
}
}
}
int[] res = new int[k];
for(int i = 0; i < k; i++){
res[i] = pq.poll();
}
return res;
}
}
时间复杂度:O(nlogk),其中 n是数组 arr 的长度。由于大根堆实时维护前 k小值,所以插入删除都是 O(logk) 的时间复杂度,最坏情况下数组里 n个数都会插入,所以一共需要 O(nlogk) 的时间复杂度。
空间复杂度:O(k),因为大根堆里最多 k 个数。
方法三:快排思想(需改变输入数组)(该方法受快排启发,不同于快排每次排序左右两侧,它每次排一侧,时间复杂度为O(n))
注意找前 K 大/前 K 小问题不需要对整个数组进行 O(NlogN) 的排序,每次对符合要求的一边排序即可
由于我们是要找到最小的k个数,可以想象,若将数组划分成前k-1个数小于第k个数,k后面的数大于第k个数,则前k个数就是小的k个数(这k个数不一定是排序的)。这自然就想到了快速排序
。
我们可以借鉴快速排序的思想
。我们知道快排的划分函数每次执行完后都能将数组分成两个部分,小于等于分界值 pivotKey 的元素的都会被放到数组的左边,大于的都会被放到数组的右边,然后返回分界值的下标 pivot。与快速排序不同
的是,快速排序会根据分界值的下标递归处理划分的两侧,而这里我们只处理划分的一边
。
我们定义函数 select_k(arr, l, r, k) 表示划分数组 arr 的 [l,r] 部分,使前 k 小的数在数组的左侧,在函数里我们调用快排的划分函数,假设划分函数返回的下标是 pivot(表示分界值 pivotKey 最终在数组中的位置),即pivotKey是数组中第pivot - l + 1 小的数,那么一共会有三种情况:
1、如果 pivot- l + 1 == k,表示pivotKey 就是第 k小的数,直接返回即可;
2、如果 pivot - l + 1 < k,表示第 k 小的数在 pivotKey 的右侧,因此递归调用 select_k(arr, pivot + 1, r, k - (pivot - l + 1));
3、如果 pivot - l + 1 > k,表示第 k小的数在 pivotKey的左侧,递归调用select_k(arr, l, pivot - 1, k)。
函数递归入口
为 randomized_selected(arr, 0, arr.length - 1, k)。在函数返回后,将前 k 个数放入答案数组返回即可。
具体步骤:
由上面分析得这是一个递归过程,则递归三部曲为:
1、递归函数功能
(原问题):
select_k(arr, l, r, k)
表示划分数组 arr 的 [l,r] 部分,使前 k 小的数在数组的左侧
2、递归终止条件
:
if (l >= r) return; 如果越界,返回
if (k == num) return; 如果划分出来的值是第k小的数,返回。
3、下一步递归(子问题):
如果 pivot - l + 1 < k,select_k(arr, pivot + 1, r, k - (pivot - l + 1));
如果 pivot - l + 1 > k,select_k(arr, l, pivot - 1, k)。
代码:
class Solution {
// 基于三数取中的划分
int partition(vector<int>& nums, int l, int r) {
int m=l+(r-l)/2;
if(nums[l]>nums[r]) swap(nums[l],nums[r]);
if(nums[m]>nums[r]) swap(nums[m],nums[r]);
if(nums[m]>nums[l]) swap(nums[m],nums[l]);
int pivotKey=nums[l];
while(l<r){
while(l<r&&nums[r]>=pivotKey){
r--;
}
swap(nums[r],nums[l]);
while(l<r&&nums[l]<=pivotKey){
l++;
}
swap(nums[r],nums[l]);
}
return l;
}
void select_k(vector<int>& arr, int l, int r, int k) {
if (l >= r) {
return;
}
int pivote = partition(arr, l, r);
int num = pivote - l + 1;
if (k == num) {
return;
} else if (k < num) {
select_k(arr, l, pivote - 1, k);
} else {
select_k(arr, pivote + 1, r, k - num);
}
}
public:
vector<int> getLeastNumbers(vector<int>& arr, int k) {
select_k(arr, 0, (int)arr.size() - 1, k);//递归入口
vector<int> vec;
for (int i = 0; i < k; ++i) {
vec.push_back(arr[i]);
}
return vec;
}
};
Java
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
if(k == 0 || arr.length == 0 || arr.length < k) return new int[0];
int[] res = new int[k];
qSort(arr , 0 , arr.length - 1 , k);
for(int i = 0 ; i < k ; i++){
res[i] = arr[i];
}
return res;
}
void qSort(int[] arr , int low , int high , int k){
int pivot;//pivot:枢轴
if(low < high){
pivot = partition(arr , low , high);//partition:划分
int num = pivot - low + 1;
if(k == num) return;
else if(k < num) qSort(arr , low , pivot - 1 , k);
else qSort(arr , pivot + 1 , high , k - num);
}
}
int partition(int[] arr , int low , int high){
int pivotkey;//pivotkey枢轴值
//三数取中确定pivotkey
//先是low和mid分别与high比较,将最大值放在high,然后比较较小的low和mid,使中间值处于low.
int mid = low + (high - low);//不用(high + low)/2是防止溢出;
if(arr[low] > arr[high]) swap(arr , low , high);
if(arr[mid] > arr[high]) swap(arr , mid , high);
if(arr[mid] > arr[low]) swap(arr , mid , low);
pivotkey = arr[low];
while(low < high){
while(low < high && arr[high] >= pivotkey) high--;
swap(arr , low ,high);
while(low < high && arr[low] <= pivotkey) low++;
swap(arr , low ,high);
}
return low;
}
void swap(int[] arr , int low , int high){
int temp = arr[low];
arr[low] = arr[high];
arr[high] = temp;
}
}
时间复杂度:O(n),
空间复杂度:O(logn)。
JZ63 数据流中的中位数
题目描述
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
题解:
首先想到要得到一组数的中位数,先对这些数排序,再取中位数就简单了。
由于数据是从数据流中读取出来的,所以数据是随时间增加的不是一次固定的。所以从读取的数据流中获取中位数可从两个方面展开(对应要实现的两个函数):
1、Insert()中不排序:--------方法一
读取的数据流直接存储到数组,不排序,在GetMedian()中实现排序再取中位数;
2、Insert()中排序:--------方法二、三
读取的数据流时边读边排序,这样GetMedian()可直接取中位数
显然Insert()中排序好,因为这样每一次读入新数据都是再有序的数组的基础上排序,而Insert()中不排序,每次GetMedian()都会对没排序的数据重新排序。
方法一:暴力方法
对于读取数据流Insert(),用vector<int> v
来实现;
对于得到中位数GetMedian(),对读取的数据vector<int> v
先排序,再取中间的数;
代码:
class Solution {
public:
vector<int> v;
void Insert(int num)
{
v.push_back(num);
}
double GetMedian()
{
sort(v.begin(), v.end());
if(v.size()&1)//用逻辑运算符判断奇偶比算术运算符快
return v[v.size()/2]*1.0;//乘1.0是因为返回的是double类型;
else return (v[v.size()/2]+v[v.size()/2-1])/2.0;//除2.0是因为返回的是double类型
}
};
时间复杂度:Insert()为O(1), GetMedian()为O(nlogn)
空间复杂度:O(n)
方法二:插入排序
对于方法一,可以发现有个优化的地方。
方法一中GetMEdian()操作,是每次都对整个vector调用排序操作。
但是其实每次都是在一个有序数组中插入一个数据。因此可以用插入排序。
所以:
Insert()操作可改为插入排序
GetMedian()操作可直接从有序数组中获取中位数
代码:
class Solution {
public:
vector<int> v;
void Insert(int num)
{
if(v.empty()) v.push_back(num);
else{
auto location=lower_bound(v.begin(), v.end(), num);
v.insert(location, num);
}
}
double GetMedian()
{
if(v.size()&1)
return v[v.size()/2]*1.0;
else return (v[v.size()/2]+v[v.size()/2-1])/2.0;
}
};
时间复杂度:Insert()为O(n),即二分查找的O(logn)和挪动数据的O(n), GetMedian()为O(1)
空间复杂度:O(n)
注:lower_bound() 和v.insert()的用法,见下面知识点
方法三:堆(左手一个大顶堆 + 右手一个小顶堆)
中位数是指:有序数组中中间的那个数(或两个树的平均)。则根据中位数可以把数组分为如下两段:
[0 … median ], [median+1 … arr.size() - 1],即[中位数的左边,中位数的右边]
我们没有必要把所有数据进行排序。只需要保证数据左半边的数都小于右半边的数,那么根据左半边的数的最大值及右半边的数的最小值
即可得到中位数。在一组数中要在O(1)时间得到最大最小值,自然想到堆
。
步骤:
1、
建立一个大顶堆A(存储左半边)
和一个小顶堆B(存储右半边)
,若数据的个数N是偶数:
A.size()=B.size()=N/2;
若数据的个数N是奇数,一半为N+1/2,一半为N-1/2:
令A.size()=N+1/2,B.size()=N-1/2【当然也可反过来】
2、Insert(num) 函数【从数据流中读取数据】:
-----插入新数据前,
当 A.size()=B.size()=N/2(即 N 为 偶数,N为正在读取的数据插入前的值
):需向 A 添加一个元素【向A中添加,是因为当N为奇数时我们令A.size()=N+1/2】。实现方法
:将新元素 num 插入至 B ,再将 B 堆顶元素插入至 A ;【这么做可以保证小顶堆的所有数永远大于等于大顶堆的 top()。因为如果先将新元素插入A,就有A.size()=N+1/2,A的大小已满足不需要再改动了,但这样不能保证新元素加入后,小顶堆的所有数还大于等于大顶堆的 top()】
-----插入新数据前,
当 A.size()不等于B.size()(即 N 为 奇数):需向 B 添加一个元素【向B中添加,是因为A.size()=N+1/2,B.size()=N-1/2,B比A少一下,所以要加入B在能保证,加入新元素后A.size()=B.size()】。实现方法
:将新元素 num插入至 A ,再将 A 堆顶元素插入至 B ;【这么做可以保证大顶堆的所有数永远小于等于小顶堆的 top()。】
整个过程我们都动态地做到了平衡两个堆的 size(),即保证它们的 size() 最大只相差了 1
。
3、GetMedian()函数:
-----当 A.size()=B.size()=N/2( N 为 偶数):则中位数为 ( A 的堆顶元素 + B 的堆顶元素 )/2。
-----当 A.size()不等于B.size()( N 为 奇数):则中位数为 A 的堆顶元素。
代码:
class Solution {
public:
priority_queue<int, vector<int>, less<int>> maxpq;
priority_queue<int, vector<int>, greater<int>> minpq;
void Insert(int num)
{
if(maxpq.size()==minpq.size()){
minpq.push(num);
maxpq.push(minpq.top());
minpq.pop();
}
else{
maxpq.push(num);
minpq.push(maxpq.top());
maxpq.pop();
}
}
double GetMedian()
{
int mid1=maxpq.top();
int mid2=minpq.top();
//注意:返回类型是double,而堆中存放的数据时int类型,所以要除以2.0,而不是2
return maxpq.size()==minpq.size()? (mid1+mid2)/2.0: mid1;
}
};
注意: GetMedian()的返回类型是double,而堆中存放的数据时int类型,所以要除以2.0,而不是2。
时间复杂度:
GetMedian()为O(1): 获取堆顶元素使用 O(1)时间;
Insert()为O(logn): 堆重建一次用时O(logN) ,每次插入新元素,两个堆的插入弹出操作共3次,即时间复杂度3O(logN) ,3可省略,即约为O(logN) 。
空间复杂度 O(N) : 其中 N 为数据流中的元素数量,大顶堆 A 和小顶堆 B 最多同时保存 N 个元素。
注: C++中的堆的数据结构见下面知识点4
JZ32 把数组排成最小的数(定义新的排序规则)
题目描述
输入一个正整数数组,把数组里所有数字拼接起来排成一个数,打印能拼接出的所有数字中最小的一个。例如输入数组{3,32,321},则打印出这三个数字能排成的最小数字为321323。
示例1
输入
[3,32,321]
返回值
"321323"
题解:
我们如果能针对“拼接”,定义一个排序规则,将数组内元素排序,然后按从小到大的顺序拼接起来,即可得到最小值;
首先,针对“拼接”要制定一个排序规则:两个元素m和n能拼接成数字mn和nm,【符号"<" , “>” , "="表示常规意义的数值大小关系,而文字的“大于” “小于” “等于” 是新定义的大小关系】
如果mn<nm,说明m拼接在前面才能使值小,定义m小于
n;
如果mn>nm,说明n拼接在前面才能使值小,定义n小于
m;
如果mn=nm,说明m拼接在前面后面一样大,定义m等于
n;
接下来,考虑怎么去拼接数字,
最后,怎么比较mn和nm的大小,因为在新制定的排序规则中,需要先对mn和nm比较才能判断m和n的大小。为了解决大数问题将m和n转换成了字符串,其拼接成的mn和nm自然也是字符串,字符串之间的比较直接用"<" , “>” , "="来操作,即可按字典序自动比较。
代码(一):使用C++内置函数sort,并更改其排序规则:
class Solution {
public:
string PrintMinNumber(vector<int> numbers) {
string ret;
if(numbers.size()<=0) return ret;
if(numbers.size()==1) return to_string(numbers[0]);
vector<string> store;
for(int val: numbers){
store.push_back(to_string(val));
}
sort(store.begin(), store.end(), strCompare);
for(string i:store){
ret+=i;
}
return ret;
}
//加static的原因:类成员函数有隐藏的this指针,static 可以去this指针????????????????
static bool strCompare(string& a, string& b){
string add1=a+b;
string add2=b+a;
return add1<add2;
}
};
代码(二):不使用内置函数,套用各种排序算法,更改其中的比较规则即可,本例以快速排序举例:
class Solution {
public:
string minNumber(vector<int>& nums) {
string ret;
if(nums.size()<=0) return ret;
if(nums.size()==1) return to_string(nums[0]);
vector<string> store;
for(int val: nums){
store.push_back(to_string(val));
}
qsort(store, 0, nums.size()-1);
for(string i:store){
ret+=i;
}
return ret;
}
void qsort(vector<string>& store, int low, int high){
int pivot;
if(low<high){
pivot=partition(store, low, high);
qsort(store,low, pivot-1);
qsort(store,pivot+1,high);
}
}
int partition(vector<string>& store, int low, int high){
//三数取中
int m=(low+high)/2;
if(store[low]+store[high]>store[high]+store[low]? true: false)
swap(store[low],store[high]);
if(store[m]+store[high]>store[high]+store[m]? true: false)
swap(store[m],store[high]);
if(store[m]+store[low]>store[low]+store[m]? true: false)
swap(store[m],store[low]);
string pivotKey=store[low];
while(low<high){
while(low<high&&store[high]+pivotKey>=pivotKey+store[high]? true: false){
high--;
}
swap(store[low],store[high]);
while(low<high&&store[low]+pivotKey<=pivotKey+store[low]? true: false){
low++;
}
swap(store[low],store[high]);
}
return low;
}
};
Java
class Solution {
String[] arr;
public String minNumber(int[] nums) {
String res = "";
arr = new String[nums.length];
for(int i = 0; i < nums.length; i++){
arr[i] = String.valueOf(nums[i]);
}
qsort(0 , arr.length - 1);
for(String val : arr){
res += val;
}
return res;
}
void qsort(int low , int high){
int pivot;
if(low < high){
pivot = partition(low , high);
qsort(low , pivot -1);
qsort(pivot + 1 , high);
}
}
int partition(int low , int high){
int mid = low + (high - low) / 2;
if((arr[low]+arr[high]).compareTo(arr[high]+arr[low]) > 0? true: false) swap(low , high);
if((arr[mid]+arr[high]).compareTo(arr[high]+arr[mid]) > 0? true: false) swap(mid , high);
if((arr[mid]+arr[low]).compareTo(arr[low]+arr[mid]) > 0? true: false) swap(low , mid);
String pivotKey = arr[low];
while(low < high){
while(low < high && (arr[high] + pivotKey).compareTo(pivotKey + arr[high]) >= 0 ? true : false) high--;
swap(low , high);
while(low < high && (arr[low] + pivotKey).compareTo(pivotKey + arr[low]) <= 0 ? true : false) low++;
swap(low , high);
}
return low;
}
void swap(int low , int high){
String temp = arr[low];
arr[low] = arr[high];
arr[high] = temp;
}
}
时间复杂度 O(NlogN) : N 为最终返回值的字符数量( strs 列表的长度 ≤N );使用快排或内置函数的平均时间复杂度为 O(NlogN) ,最差为 O(N^2)。
空间复杂度 O(N) : 字符串列表 strs 占用线性大小的额外空间。
注:sort的自定义排序规则见下方 知识点6.
面试题51 数组中的逆序对
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例 1:
输入: [7,5,6,4]
输出: 5
题解:
1、本题其实就是在对一个数组进行归并排序
的基础上,增加了一个统计逆序对数目的障眼法,其实还是归并排序。
2、如果了解归并排序的话,就会想到我们可以用分治的思想,将给定的 nums 先一分为二,统计左半部分的逆序对数目,再统计右半部分的逆序对数目,以及统计跨越左半部分和右半部分的逆序对数目,然后将三者相加即可。
具体参考视频详解
代码:
class Solution {
public:
int reversePairs(vector<int>& nums) {
int lenght=nums.size();
if(lenght<=1) return 0;
vector<int> numsCopy=nums;
vector<int> temp(lenght);
int count=mergeSort(numsCopy, temp, 0, lenght-1);
return count;
}
int mergeSort(vector<int>& nums, vector<int>& temp, int left, int right){
if(left==right){
return 0;
}
int mid=left+(right-left)/2;//不用 int mid=(left+right)/2是为了防止溢出;
int leftCount=mergeSort(nums, temp, left, mid);
int rightCount=mergeSort(nums, temp, mid+1, right);
if(nums[mid] <= nums[mid+1]){
return leftCount+rightCount;
}
int crossCount=merge(nums, temp, left, right, mid);
return leftCount+rightCount+crossCount;
}
int merge(vector<int>& nums, vector<int>& temp, int left, int right, int mid){
for(int i = left; i <= right; i++) temp[i] = nums[i];
int i=left,j=mid+1,pos=left;
int count=0;
while(i<=mid&&j<=right){
if(temp[i]<=temp[j]){
nums[pos]=temp[i];
i++;
pos++;
}
else{
nums[pos]=temp[j];
count+=mid-i+1;
j++;
pos++;
}
}
if(i<=mid){
for(int k=i;k<=mid;k++){
nums[pos]=temp[k];
pos++;
}
}
if(j<=right){
for(int k=j;k<=right;k++){
nums[pos]=temp[k];
pos++;
}
}
return count;
}
};
java:
class Solution {
public int reversePairs(int[] nums) {
if(nums.length <= 1) return 0;
int[] temp = new int[nums.length];
int res = Msort(nums , temp , 0 , nums.length - 1);
return res;
}
int Msort(int[] nums , int[] temp , int low , int high){
if(low == high) return 0;
else{
int mid = low + (high - low) / 2;
int leftCount = Msort(nums , temp , low , mid);
int rightCount = Msort(nums , temp , mid + 1 , high);
//注意这里不能是temp[mid] <= temp[mid + 1],因为向temp里添加元素是在下面Merge函数里,这里还没添加。所以要用num
if(nums[mid] <= nums[mid + 1]){
return leftCount + rightCount;
}
int crossCount = Merge(temp , nums , low , mid ,high);
return leftCount + rightCount + crossCount;
}
}
int Merge(int[] temp , int[] nums , int low , int mid , int high){
for(int i = low ; i <= high ; i++){
temp[i] = nums[i];
}
int count = 0;
int i = low , j = mid + 1 , pos = low;
for(; i <= mid && j <= high ; pos++){
if(temp[i] <= temp[j]){
nums[pos] = temp[i++];
}else{
nums[pos] = temp[j++];
count += (mid - i + 1);
}
}
if(i <= mid){
for(int k = i ; k <= mid ; k++){
nums[pos++] = temp[k];
}
}
if(j <= high){
for(int k = j ; k <= high ; k++){
nums[pos++] = temp[k];
}
}
return count;
}
}
时间复杂度:同归并排序 O(nlogn)。
空间复杂度:同归并排序 O(n),因为归并排序需要用到一个临时数组O(n)和递推栈O(logn),共O(n+logn),因O(logn)和O(n)比可不计,所以空间复杂度为O(n)。
面试题61:扑克牌中的顺子
示例 1:
输入: [1,2,3,4,5]
输出: True
示例 2:
输入: [0,0,1,2,5]
输出: True
题解
抽象建模:
思路:
先将数组排序,排序后【除大小王(即,0)外】若有重复元素,则肯定不是顺子;若无重复元素,最大值和最小值的差值小于5,则是顺子,否则不是;
或【代码参考】
代码:
class Solution {
public:
bool isStraight(vector<int>& nums) {
sort(nums.begin(), nums.end());
for(int i=0; i<nums.size()-1; i++){
if(nums[i]==0) continue;
else if(nums[i]==nums[i+1]) return false;
}
int max=nums[nums.size()-1];
int min;
for(int i=0; i<nums.size(); i++){
if(nums[i]==0) continue;
min=nums[i]; break;
}
if((max-min)<5) return true;
else return false;
}
};
知识点
1、vector的添加元素
若vector ret,这只是定义,ret是空的,需用ret.push_back()来添加元素
若vector ret(k)或vector ret(k,0),其执行了初始化,ret开始就有K个0,其添加元素需用下标形式,即ret[i]=arr[i],若用ret.push_back()是在0后继续添加。
2、vector.insert()
insert() 函数有以下三种用法:
-----在指定位置loc前
插入值为val的元素,返回指向这个元素的迭代器
v.insert(loc, val)
-----指定位置loc前插入num个值为val的元素
v.insert(loc,num, val)
-----在指定位置loc前插入区间[start, end)的所有元素
3、堆
4、C++中的堆:优先队列priority_queue
这
5、lower_bound()和upper_bound( )
lower_bound( )和upper_bound( )都是利用二分查找
的方法在一个排好序的数组
中进行查找的。
在从小到大的排序数组中,
lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于
num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于
num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。