剑指offer目前遇到的函数

c++常见函数

二分查找算法的要求:

  1. 必须采用顺序存储结构
  2. 必须按关键字大小有序排列

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; 
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值