c++常见函数
二分查找算法的要求:
- 必须采
用顺序存储结构
。- 必须
按关键字大小有序排列
。
1、题目:最长递增子序列
//包含在<algorithm>
中:
lower_bound( )和upper_bound( )都是利用二分查找O(logn)
的方法在一个排好序的数组中
进行查找的。
1.1 在从小到大的排序数组中,
(1)lower_bound(begin, end, val):
它的作用是返回有序数组begin…end中第一个大于等于val
的元素的迭代器
lower_bound( begin,end,num):
从数组的begin位置到end-1位置二分查找第一个大于或等于num
的数字,找到返回该数字的地址
,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
(2)upper_bound( begin,end,num):
从数组的begin位置到end-1位置二分查找第一个大于num
的数字,找到返回该数字的地址
,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
1.2 在从大到小的排序数组中,
(1)lower_bound( begin,end,num,greater<type>() ):
从数组的begin位置到end-1位置
二分查找第一个小于或等于num
的数字,找到返回该数字的地址
,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
(2)upper_bound( begin,end,num,greater<type>() ):
从数组的begin位置到end-1位置
二分查找第一个小于num
的数字,找到返回该数字的地址
,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
#include<bits/stdc++.h> // 万能头文件
using namespace std;
// 自定义比较函数
int cmd(int a,int b){
return a>b;
}
int main(){
int num[6]={1,2,4,7,15,34};
sort(num,num+6); //按从小到大排序(快排,O(nlogn))。结果:1,2,4,7,15,34
int pos1=lower_bound(num,num+6,7)-num; //返回数组中第一个大于或等于被查数的值对应的下标。注:因lower_bound函数返回的是地址,与数组首地址相减,得到目标元素的索引
int pos2=upper_bound(num,num+6,7)-num; //返回数组中第一个大于被查数的值对应的下标
cout<<pos1<<" "<<num[pos1]<<endl;
cout<<pos2<<" "<<num[pos2]<<endl;
// 出错情况:原数组为从大到小有序,未加入greater<type>()参数,会出错
vector<int> num_={34,15,7,4,2,1};
int pos1_=lower_bound(num_.begin(),num_.end(),7)-num_.begin(); //返回数组中第一个大于或等于被查数的值对应的下标
int pos2_=upper_bound(num_.begin(),num_.end(),7)-num_.begin(); //返回数组中第一个大于被查数的值对应的下标
cout<<pos1_<<" "<<num_[pos1_]<<endl;
cout<<pos2_<<" "<<num_[pos2_]<<endl;
sort(num,num+6,cmd); //按从大到小排序。结果:34,15,7,4,2,1
int pos3=lower_bound(num,num+6,7,greater<int>())-num; //返回数组中第一个小于或等于被查数的值
int pos4=upper_bound(num,num+6,7,greater<int>())-num; //返回数组中第一个小于被查数的值
cout<<pos3<<" "<<num[pos3]<<endl;
cout<<pos4<<" "<<num[pos4]<<endl;
return 0;
}
结果:
3 7
4 15
6 57713 // 索引越界
6 57713
2 7
3 4
eg. int pos = lower_bound(end.begin(), end.end(), arr[i]) - end.begin();
(3)emplace_back(val):
作用同push_back
,效率更高
eg.current_max.emplace_back(1);
2、题目:大数加法
swap(a,b):
交换a与b的值。swap函数交换能力特别强,可以交换整个数组的值。无返回值
。
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int a[5]={1,1,1,1,1},b[5]={2,2,2,2,2};
swap(a,b);
cout<<a[0]<<" "<<a[4]<<endl;
swap(a[0],b[0]);
cout<<b[0]<<" "<<b[4]<<endl;
return 0;
}
结果:
2 2
2 1
3、题目:二叉树的之字形层序遍历
reverse(first,last)
:逆序(或反转),多用于字符串、数组、容器。
头文件是#include <algorithm>
。
用于反转在[first,last)范围内
的顺序(包括first指向的元素,不包括last指向的元素)。
无返回值
。
容器类型的要用begin()和end()来指定反转的区域,数组类型的直接用int类型即可。
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
int main()
{
int a[5]={1,2,3,4,5};
reverse(a,a+5); // 数组a变为:5,4,3,2,1
string str="hello world , hi";
reverse(str.begin(),str.end()); // str结果为:ih , dlrow olleh
cout<<str<<endl;
vector<int> v = {5,4,3,2,1};
reverse(v.begin(),v.end()); // 容器v的值变为:1,2,3,4,5
reverse(v.begin(),v.begin()+2); // 容器v的值变为:2,1,3,4,5
}
4、题目:最长公共子串
substr(start , [length])
用途:一种构造string的方法
形式:s.substr(pos, n)
解释:返回一个string
,包含s中从pos开始的n个字符的拷贝(pos的默认值是0,n的默认值是s.size() - pos,即不加参数会默认拷贝整个s)
补充:若pos的值超过了string的大小,则substr函数会抛出一个out_of_range异常;若pos+n的值超过了string的大小,则substr会调整n的值,只拷贝到string的末尾
#include<string>
#include<iostream>
using namespace std;
int main()
{
string s("12345asdf");
string a = s.substr(0,5); //获得字符串s中从第0位开始的长度为5的字符串,a="12345"
cout << a << endl;
string s2 = "0123456789";
string sub1 = s2.substr(5); //只有一个数字5表示从下标为5开始一直到结尾:sub1 = "56789"
string sub2 = s2.substr(5, 3); //从下标为5开始截取长度为3位:sub2 = "567"
string sub3 = s2.substr(5, 10); //从下标为5开始截取长度为10位:sub3 = "56789"。长度越界,自动截取到最末尾位置
string sub4 = s2.substr(10, 3); //从下标为10开始截取长度为3位:下标越界,抛出一个out_of_range异常(我的结果为空)
}
5、题目:扑克牌顺子
sort(start,end,[排序方法]):
它使用的排序方法是类似于快排
的方法,时间复杂度为O(nlogn)
,执行效率较高!
三个参数:
(1)第一个是要排序的数组的起始地址。
(2)第二个是结束的地址(最后一位要排序的地址+1)
(3)第三个参数是排序的方法,可以是从大到小也可是从小到大,还可以不写第三个参数,此时默认的排序方法是从小到大排序。
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int a[10]={9,6,3,8,5,2,7,4,1,0};
sort(a,a+10); // 结果:0,1,2,3,4,5,6,7,8,9
return 0;
}
A、改变第三个参数
eg.加入一个自定义比较函数
complare(),此函数的实现过程是这样的
#include<iostream>
#include<algorithm>
using namespace std;
// 自定义的比较函数:从大到小
bool complare(int a,int b)
{
return a>b;
}
int main()
{
int a[10]={9,6,3,8,5,2,7,4,1,0};
sort(a,a+10,complare); // 在这里就不需要对complare函数传入参数了,//这是规则
for(int i=0;i<10;i++)
cout<<a[i]<<",";
return 0;
}
结果:
9,8,7,6,5,4,3,2,1,0,
B、改变第三个参数
less<数据类型>():
//从小到大排序
greater<数据类型>():
//从大到小排序
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int a[10]={9,6,3,8,5,2,7,4,1,0};
sort(a,a+10,less<int>());
for(int i=0;i<10;i++)
cout<<a[i]<<",";
return 0;
}
结果:
0,1,2,3,4,5,6,7,8,9,(等价于sort(a,a+10))
#include<iostream>
#include<algorithm>
using namespace std;
int main()
{
int a[10]={9,6,3,8,5,2,7,4,1,0};
sort(a,a+10,greater<int>());
for(int i=0;i<10;i++)
cout<<a[i]<<",";
return 0;
}
结果:
9,8,7,6,5,4,3,2,1,0,(等价于sort(a,a+10,complare))
eg.sort(numbers.begin(),numbers.end());
6、题目:孩子们的游戏(圆圈中最后剩下的数)
(1)advance(it, n):
it 表示某个迭代器,n 为整数。该函数的功能是将 it 迭代器前进或后退 n 个位置
。
注意:advance() 函数本身不会检测 it 迭代器移动 n 个位置的可行性
,如果 it 迭代器的移动位置超出了合理范围,it 迭代器的指向将无法保证,此时使用 *it 将会导致程序崩溃。
(2)distance(first, last):
first 和 last 都是迭代器
,该函数的功能是计算 first 和 last 之间的距离。
#include <iostream> // std::cout
#include <iterator> // std::advance
#include <forward_list>
using namespace std;
int main() {
//创建一个 forward_list 容器,仅支持使用前向迭代器
forward_list<int> mylist{1,2,3,4};
//it为前向迭代器,其指向 mylist 容器中第一个元素
forward_list<int>::iterator it = mylist.begin();
//借助 advance() 函数将 it 迭代器前进 2 个位置
advance(it, 2);
cout << "*it = " << *it;
return 0;
}
结果:*it = 3
此程序中,由于 it 为前向迭代器,其只能进行 ++ 操作,即只能前进(右移),所以 advance() 函数的第 2 个参数只能为正数。
#include <iostream> // std::cout
#include <iterator> // std::advance
#include <vector>
using namespace std;
int main() {
//创建一个 vector 容器
vector<int> myvector{1,2,3,4};
//it为随机访问迭代器,其指向 myvector 容器中第一个元素
vector<int>::iterator it = myvector.begin();
//借助 advance() 函数将 it 迭代器前进 2 个位置
advance(it, 2);
cout << "1、*it = " << *it << endl;
//继续使用it,其指向 myvector 容器中最后一个元素之后的位置
it = myvector.end();
//借助 advance() 函数将 it 迭代器后退 3 个位置
advance(it, -3);
cout << "2、*it = " << *it;
return 0;
}
结果:
1、*it = 3
2、*it = 2
7、题目: 积压订单中的订单总数(leecode)
定义:priority_queue<Type, Container, Functional>
Type 就是数据类型,Container 就是容器类型(Container必须是用数组实现的容器,比如vector,deque等等,但不能用 list。STL里面默认用的是vector),Functional 就是比较的方式,当需要用自定义的数据类型时才需要传入这三个参数,使用基本数据类型时,只需要传入数据类型,默认是大顶堆。
一般是:
//升序队列
priority_queue <int,vector<int>,greater<int> > q;
//降序队列
priority_queue <int,vector<int>,less<int> >q;
//greater和less是std实现的两个仿函数(就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了)
仿函数:就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了。
和队列基本操作相同:
top() 访问队头元素
empty() 队列是否为空
size() 返回队列内元素个数
push() 插入元素到队尾 (并排序)
emplace() 原地构造一个元素并插入队列
pop() 弹出队头元素
swap() 交换内容
class Solution {
public:
// 这个题应该可以用堆来做
typedef pair<int, int> PII; // 每个pair对象可以存储两个值,这两个值可以是不同的数据类型。
typedef long long LL;
#define x first
#define y second
static const int mod = 1e9 + 7;
//对于基础类型 默认是大顶堆
priority_queue<PII> a;
//等同于 priority_queue<PII, vector<PII>, less<PII>> buy; // 大根堆
priority_queue<PII, vector<PII>, greater<PII>> shell; // 小根堆
int getNumberOfBacklogOrders(vector<vector<int>>& orders) {
LL sum = 0, cnt = 0; // sum:采购订单数+销售订单数;cnt:删除的订单数
for (auto order : orders) {
int price = order[0], amount = order[1], oderType=order[2];
sum += amount;
if (oderType == 0)
buy.push({price, amount});
else
shell.push({price, amount});
while (buy.size()) {
auto t1 = buy.top(); // 先取出价值最大的采购订单
if (shell.size() == 0)
break;
auto t2 = shell.top();// 取出价值最小的销售订单
if (t1.x < t2.x)
break;
// 匹配
else {
buy.pop(), shell.pop();
if (t1.y > t2.y) { // [15, 5], [15, 2]
buy.push({t1.x, t1.y - t2.y}); // 还剩那么多
} else if (t1.y < t2.y) {
shell.push({t2.x, t2.y - t1.y});
}
cnt += 2 * min(t1.y, t2.y); // 记录消去了几个订单
}
}
}
return (sum - cnt) % mod;
}
};