13届蓝桥杯c++组日常练习问题记录
以下内容皆收集于相关博客,如有侵权请联系我删除!
一、常用函数
1、C++ STL中 next_permutation函数的用法
1.1 输出序列{1,2,3,4}字典序的全排列。
#include <iostream>
#include<algorithm>
using namespace std;
int main(int argc, char** argv) {
int a[4]={1,2,3,4};
sort(a,a+4);
do{
//cout<<a[0]<<" "<<a[1]<<" "<<a[2]<<" "<<a[3]<<endl;
for(int i=0;i<4;i++)
cout<<a[i]<<" ";
cout<<endl;
}while(next_permutation(a,a+4));
return 0;
1.2、排列组合数公式
1.3 、C++ 计算组合数(动态规划)
int c_m_n(int m,int n) {
vector<vector<int> >dp(m+1, vector<int>(n+1,0));
for (int j = 0; j <= n;++j) {
dp[j][j] = 1;
for (int i = j + 1;i <= m;++i) {
if (j == 0)
dp[i][j] = 1;
else
dp[i][j] = dp[i - 1][j] + dp[i - 1][j - 1];
}
}
return dp[m][n];
}
2、C++ 保留两位小数(setprecision(n)的一些用法总结)
#include <iomanip> //不要忘了头文件
//第一种写法
cout<<setiosflags(ios::fixed)<<setprecision(2);
//第二种写法
cout.setf(ios::fixed);
cout<<setprecision(2);
//第三种写法
cout<<fixed<<setprecision(2);
上面的语句写一次就行了,对之后的数字都有效。
3、sort函数
1.sort函数包含在头文件为#include< algorithm>
的c++标准库中,调用标准库里的排序方法可以实现对数据的排序,但是sort函数是如何实现的,我们不用考虑!
2.sort函数的模板有三个参数:
void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);
(1)第一个参数first:是要排序的数组的起始地址。
(2)第二个参数last:是结束的地址(最后一个数据的后一个数据的地址)
(3)第三个参数comp是排序的方法:可以是从升序也可是降序。如果第三个参数不写,则默认的排序方法是从小到大排序。
class Solution {
public:
int eraseOverlapIntervals(vector<vector<int>>& intervals) {
if (intervals.empty()) {
return 0;
}
sort(intervals.begin(), intervals.end(), [](const auto& a, const auto& b) {
return a[1] < b[1];
});
int size = intervals.size();
int ans = 0;
int pre = intervals[0][1];
for (int i = 1;i < size;++i) {
if (intervals[i][0] < pre) {
++ans;
}
else {
pre = intervals[i][1];
}
}
return ans;
}
};
4、C++的STL中accumulate的用法
4.1 累加求和
accumulate带有三个形参:头两个形参指定要累加的元素范围,第三个形参则是累加的初值。
accumulate函数将它的一个内部变量设置为指定的初始值,然后在此初值上累加输入范围内所有元素的值。accumulate算法返回累加的结果,其返回类型就是其第三个实参的类型。
#include<numeric>
int sum = accumulate(vec.begin() , vec.end() , 42);
4.2 连接字符串
可以使用accumulate把string型的vector容器中的元素连接起来:
string sum = accumulate(v.begin() , v.end() , string(" "));
这个函数调用的效果是:从空字符串开始,把vec里的每个元素连接成一个字符串。
5、getline()
二.对于string类
方法一:getline(cin, str)
这说明这里的getline不是类方法。
在这里要注意的是:当 getline(cin, str);前面的输入是cin>>ss;的话,那么此处str的值时空的,因为他会读取上一行的结束符。
1 #include <iostream>
2 #include <string>
3 using namespace std;
4
5 int main()
6 {
7 string str;
8 getline(cin, str);
9 cout << str << endl;
10 return 0;
11 }
6、stringstream用于数据类型转换
stringtream最常用于string与各种内置数据类型的转换。
示例代码如下:
#include <iostream>
#include <sstream>
#include <string>
#include<cstdlib>
using namespace std;
int main()
{
stringstream sstr;
//--------int转string-----------
int a=100;
string str;
sstr<<a;
sstr>>str;
cout<<str<<endl;
//--------string转char[]--------
sstr.clear();//如果你想通过使用同一stringstream对象实现多种类型的转换,请注意在每一次转换之后都必须调用clear()成员函数。
string name = "colinguan";
char cname[200];
sstr<<name;
sstr>>cname;
cout<<cname;
system("pause");
}
123456789101112131415161718192021222324
string转double/int:
#include <iostream>
#include <sstream>
using namespace std;
int main()
{
double dVal;
int iVal;
string str;
stringstream ss;
// string -> double
str = "123.456789";
ss << str;
ss >> dVal;
cout << "dVal: " << dVal << endl;
// string -> int
str = "654321";
ss.clear();
ss << str;
ss >> iVal;
cout << "iVal: " << iVal << endl;
return 0;
}
7、substr函数
原型:string substr ( size_t pos = 0, size_t n = npos ) const;
功能:获得子字符串。
参数说明:pos为起始位置(默认为0),n为结束位置(默认为npos)
S.substr(ans_l,ans)
从ans_l开始的ans个字符串
返回值:子字符串
#include <iostream>
#include <string>
#include <vector>
//字符串分割函数
std::vector<std::string> split(std::string str, std::string pattern)
{
std::string::size_type pos;
std::vector<std::string> result;
str += pattern;//扩展字符串以方便操作
int size = str.size();
for (int i = 0; i < size; i++)
{
pos = str.find(pattern, i);
if (pos < size)
{
std::string s = str.substr(i, pos - i);
result.push_back(s);
i = pos + pattern.size() - 1;
}
}
return result;
}
8、insert函数()
string &insert(int p0, const char *s);——在p0位置插入字符串s
string &insert(int p0, const char *s, int n);——在p0位置插入字符串s的前n个字符
string &insert(int p0,const string &s);——在p0位置插入字符串s
string &insert(int p0,const string &s, int pos, int n);——在p0位置插入字符串s从pos开始的连续n个字符
string &insert(int p0, int n, char c);//在p0处插入n个字符c
iterator insert(iterator it, char c);//在it处插入字符c,返回插入后迭代器的位置
void insert(iterator it, const_iterator first, const_iteratorlast);//在it处插入从first开始至last-1的所有字符
void insert(iterator it, int n, char c);//在it处插入n个字符c
8、对数函数 log() 操作
首先要知道exp()函数
exp(n)值为e^n次方;
另外log函数包括两种函数 一种以e为低的log()函数
另一种为以10为底的log 10()函数;
具体用法见下面这个小程序
#include<iostream>
#include<cmath>
using namespace std;
int main()
{
double a=9,b=10;
cout<<log(a)<<endl;
cout<<log(exp(a))<<endl;
cout<<log10(b)<<endl;
return 0;
}
另外如果自定义以m为底,求log n的值
需要double a=log(n)/log(m);
举例如下:
#include<iostream>
#include<cmath>
using namespace std;
int main()
{
double a=2,b=2;//以2为底的对数函数
for(b=2;b<=16;b=b+2)
{
cout<<"b="<<b<<"时,以2为底的对数函数="<<log(b)/log(a)<<endl;
}
return 0;
}
###
9、如何检查浮点数是否为整数
Number 1: 向下向上取整法
int main() {
float num1 = 10.0f;
float num2 = 2.0f;
double res = num1 / num2;
if(ceil(res) == floor(res))
std::cout << "整数" << std::endl;
else
std::cout << "小数" << std::endl;
return 0;
}
头文件 <cmath>
ceil():Computes the smallest integer value not less than arg. —— 向上取整
floor() : Computes the largest integer value not greater than arg. —— 向下取整
向上,向下取整后相同,则为整数
10、partial_sum 的使用
partial_sum 是C++ STL 算法组件中的其中一个算法,其作用是计算某个序列局部元素的和。它有四个重载函数。要使用 partial_sum 需要引用头文件 numeric。
/ 局部总和 第一个重载
//partial_sum(容器要计算的起始位置,容器要计算的结束位置,结果存放的起始位置)
partial_sum(vec.begin(), vec.end(), arr);
cout << "1 arr : \n";
for (int i = 0; i < 10; i++) { cout << arr[i] << '\t'; }//输出
cout << endl << endl;
//第二个重载
// partial_sum(容器要计算的起始位置,容器要计算的结束位置,结果存放的起始位置,自定义函数)
partial_sum(vec.begin(), vec.end(), arr, func);
cout << "2 arr : \n";
for (int i = 0; i < 10; i++) { cout << arr[i] << '\t'; }//输出
cout << endl << endl;
//第三个重载
//partial_sum(容器要计算的起始位置,容器要计算的结束位置,结果存放的起始位置)
partial_sum(vec.begin(), vec.end(), vec2.begin());
cout << "3 vec2 : \n";
for (int i = 0; i < 10; i++) { cout << vec2[i] << '\t'; }//输出
cout << endl << endl;
//第四个重载
// partial_sum(容器要计算的起始位置,容器要计算的结束位置,结果存放的起始位置,自定义函数)
partial_sum(vec.begin(), vec.end(), vec2.begin(), func);
cout << "4 vec2 : \n";
for (int i = 0; i < 10; i++) { cout << vec2[i] << '\t'; }//输出
cout << endl<< endl;
————————————————
版权声明:本文为优快云博主「思不凉」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/weixin_44566320/article/details/91355171
11、关于lower_bound( )和upper_bound( )的常见用法
lower_bound( )和upper_bound( )都是利用二分查找的方法在一个排好序的数组中进行查找的。
lower_bound() 函数定义在头文件中l
lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
11、c++中二进制和整数转化
#1,包含文件
#include< bitset >
#2,整数转化成二进制
int a = 63;
bitset<6> bs(a);
#3,二进制转化成整数
int b = bs.to_ullong();
#4,string转bitset
*// 推荐写法* std::bitset<6>(std::string("110101")); *// 而不是* std::bitset<6>("110101");
12、string类型反转
Using inbuilt “reverse” function:
There is a direct function in “algorithm” header file for doing reverse that saves our time when programming.
// A quickly written program for reversing a string
// using reverse()
#include <bits/stdc++.h>
using namespace std;
int main()
{
string str = "geeksforgeeks";
// Reverse str[begin..end]
reverse(str.begin(), str.end());
cout << str;
return 0;
}
13、 c++二分查找
1.头文件
#include <algorithm>
2.使用方法
1.binary_search:查找某个元素是否出现。
a.函数模板:binary_search(arr[],arr[]+size , indx)
b.参数说明:
arr[]: 数组首地址
size:数组元素个数
indx:需要查找的值
c.函数功能: 在数组中以二分法检索的方式查找,若在数组(要求数组元素非递减)中查找到indx元素则真,若查找不到则返回值为假。
2.lower_bound:查找第一个大于或等于某个元素的位置。
a.函数模板:lower_bound(arr[],arr[]+size , indx):
b.参数说明:
arr[]: 数组首地址
size:数组元素个数
indx:需要查找的值
c.函数功能: 函数lower_bound()在first和last中的前闭后开区间进行二分查找,返回大于或等于val的第一个元素位置(注意是地址)。如果所有元素都小于val,则返回last的位置
d.举例如下:
一个数组number序列为:4,10,11,30,69,70,96,100.设要插入数字3,9,111.pos为要插入的位置的下标,则
/注意因为返回值是一个指针,所以减去数组的指针就是int变量了/
pos = lower_bound( number, number + 8, 3) - number,pos = 0.即number数组的下标为0的位置。
pos = lower_bound( number, number + 8, 9) - number, pos = 1,即number数组的下标为1的位置(即10所在的位置)。
pos = lower_bound( number, number + 8, 111) - number, pos = 8,即number数组的下标为8的位置(但下标上限为7,所以返回最后一个元素的下一个元素)。
e.注意:函数lower_bound()在first和last中的前闭后开区间进行二分查找,返回大于或等于val的第一个元素位置。如果所有元素都小于val,则返回last的位置,且last的位置是越界的!
返回查找元素的第一个可安插位置,也就是“元素值>=查找值”的第一个元素的位置
3.upper_bound:查找第一个大于某个元素的位置。
a.函数模板:upper_bound(arr[],arr[]+size , indx):
b.参数说明:
arr[]: 数组首地址
size:数组元素个数
indx:需要查找的值
c.函数功能:函数upper_bound()返回的在前闭后开区间查找的关键字的上界,返回大于val的第一个元素位置
例如:一个数组number序列1,2,2,4.upper_bound(2)后,返回的位置是3(下标)也就是4所在的位置,同样,如果插入元素大于数组中全部元素,返回的是last。(注意:数组下标越界)
返回查找元素的最后一个可安插位置,也就是“元素值>查找值”的第一个元素的位置 。
14、C++ cout格式化输出(输出格式)
关于流操纵算子的使用,来看下面的程序。
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
int n = 141;
//1) 分别以十六进制、十进制、八进制先后输出 n
cout << "1)" << hex << n << " " << dec << n << " " << oct << n << endl;
double x = 1234567.89, y = 12.34567;
//2)保留5位有效数字
cout << "2)" << setprecision(5) << x << " " << y << " " << endl;
//3)保留小数点后面5位
cout << "3)" << fixed << setprecision(5) << x << " " << y << endl;
//4)科学计数法输出,且保留小数点后面5位
cout << "4)" << scientific << setprecision(5) << x << " " << y << endl;
//5)非负数显示正号,输出宽度为12字符,宽度不足则用 * 填补
cout << "5)" << showpos << fixed << setw(12) << setfill('*') << 12.1 << endl;
//6)非负数不显示正号,输出宽度为12字符,宽度不足则右边用填充字符填充
cout << "6)" << noshowpos << setw(12) << left << 12.1 << endl;
//7)输出宽度为 12 字符,宽度不足则左边用填充字符填充
cout << "7)" << setw(12) << right << 12.1 << endl;
//8)宽度不足时,负号和数值分列左右,中间用填充字符填充
cout << "8)" << setw(12) << internal << -12.1 << endl;
cout << "9)" << 12.1 << endl;
return 0;
}
程序的输出结果是:
1)8d 141 215
2)1.2346e+06 12.346
3)1234567.89000 12.34567
4)1.23457e+06 1.23457e+01
5)+12.10000
6)12.10000*
7)****12.10000
8)-***12.10000
9)12.10000
15、C++中将string按照空白字符分割的新方法
首先要引入头文件,C++标准库中的提供了比ANSI C的<stdio.h>更高级的一些功能,即单纯性、类型安全和可扩展性。
库是最近才被列入C++标准的。(不要把与标准发布前被删掉的弄混了。)因此,老一点的编译器,如GCC2.95,并不支持它。如果你恰好正在使用这样的编译器而又想使用的话,就要先对它进行升级更新。
库定义了三种类:istringstream、ostringstream和stringstream,分别用来进行流的输入、输出和输入输出操作。另外,每个类都有一个对应的宽字符集版本。简单起见,我主要以stringstream为中心,因为每个转换都要涉及到输入和输出操作。
注意,使用string对象来代替字符数组。这样可以避免缓冲区溢出的危险。而且,传入参数和目标对象的类型被自动推导出来,即使使用了不正确的格式化符也没有危险。
————————————————
版权声明:本文为优快云博主「猫小时候」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/onever_say_love/article/details/49123935
#include<iostream>
#include<string>
#include<sstream>
#include<vector>
using namespace std;
int main(){
//用于存放分割后的字符串
vector<string> res;
//待分割的字符串,含有很多空格
string word=" Hello, I want to learn C++! ";
//暂存从word中读取的字符串
string result;
//将字符串读到input中
stringstream input(word);
//依次输出到result中,并存入res中
while(input>>result)
res.push_back(result);
//输出res
for(int i=0;i<res.size();i++){
cout<<res[i]<<endl;
}
return 0;
}
二、常用方法和数据结构
1、 C++ 输入一行个数未知的整数
这种情况容易在在线笔试中遇到:输入一行整数,个数未知,整数之间用空格间隔
,除了字符串分割提取外,可以采用如下简便方式:
int main() {
vector<int> inputs;
int tmp;
cin >> tmp;
inputs.push_back(tmp);
while (cin.get() != '\n') {
cin >> tmp;
inputs.push_back(tmp);
}
return 0;
}
2、初始化链表
#include<iostream>
using namespace std;
class ListNode {
public:
int val;
ListNode* next = NULL;
};
int main() {
int num[4] = { 3,2,0,-4 };
//初始化链表
ListNode* head = new ListNode();
ListNode* p1 = new ListNode();
ListNode* p2 = new ListNode();
for (int i = 0;i < 4;++i) {
p1->val = num[i];
p1->next = p2;
if (i == 0) head = p1;
p1 = p2;
p2 = new ListNode();
}
ListNode* p = head;
while (p->next != NULL) {
cout << p->val << " ";
p = p->next;
}
}
3 、Vector创建和初始化数组的方法:
3.1 一维vector
3.1.1 创建一维vector:
vector<int> nums;//不指定长度vector<int> nums(n); // 指定长度为n
添加元素
nums.push_back(1);//直接从数组末端添加nums[i] = 1;//直接赋值给第i个位置
删除元素
nums.resize(nums.size-i); //直接将数组长度减小,某种方式上删掉了后面i个nums.pop_back();//删掉最后一个元素
3.1.2 数组遍历
for(int i = 0; i < nums.size(); i++){ cout<<nums[i]<<endl;}
3.1.3 其他
获得长度:nums.size()
排序(O(nlogn)):sort(nums.begin(),nums.end());
翻转:reverse(nums.begin(), nums.end());
合并两个vector:合并nums1和nums2,并将合并后的数组赋值给nums
vector<int> nums1(m),nums2(n);
vector<int> nums;
nums.resize(m+n);
merge(nums1.begin(), nums1.end(),nums2.begin(),nums2.end(),nums);
3.2 二维vector
3.2.1 创建m*n的二维vector: 直接定义
vector<vector <int> > nums(m ,vector<int>(n)); //m*n的二维vector
定义了一个vector容器,元素类型为vector<int>
,初始化为包含m个vector<int>
对象,每个对象都是一个新创立的vector<int>
对象的拷贝,而这个新创立的vector<int>
对象被初始化为包含n个0。vector<int>(n)
; 表示构造一个无名且含n个0的vector<int>
对象。
3.2.2 动态创建m*n的二维vector
方法一:
vector<vector <int> > nums;nums.resize(m);for(int i=0;i<m;i++) nums[i].resize(n);
方法二:
vector<vector <int> > nums;
nums.resize(m,vector<(n));
方法三:
vector v[maxn]; //备注:node 是结构体; maxn 是v数组里元素的个数
3.2.3 初始化二维数组
vector<vector <int> > nums(m ,vector<int>(n,0)); //m*n的二维vector,所有元素为0
获得二维数组的行数:nums.size();
获得二维数组的列数:nums[0].size()
3.2.4 数组遍历
int m = nums.size(),n = nums[0].size();
for(int i = 0; i < m; i++)
{
for(int j = 0; j < n; j++)
{
cout<<nums[i][j]<<endl;
}
}
4、队列queue
queue 的生成方式和 stack 相同,下面展示如何创建一个保存字符串对象的 queue:
std::queue<std::string> words;
也可以使用拷贝构造函数:
std::queue<std::string> copy_words {words}; // A duplicate of words
stack、queue 这类适配器类都默认封装了一个 deque 容器,也可以通过指定第二个模板类型参数来使用其他类型的容器:
std::queue<std::string, std::list<std::string>>words;
底层容器必须提供这些操作:front()、back()、push_back()、pop_front()、empty() 和 size()。
4.1 queue 操作
queue 和 stack 有一些成员函数相似,但在一些情况下,工作方式有些不同:
- front():返回 queue 中第一个元素的引用。如果 queue 是常量,就返回一个常引用;如果 queue 为空,返回值是未定义的。
- back():返回 queue 中最后一个元素的引用。如果 queue 是常量,就返回一个常引用;如果 queue 为空,返回值是未定义的。
- push(const T& obj):在 queue 的尾部添加一个元素的副本。这是通过调用底层容器的成员函数 push_back() 来完成的。
- push(T&& obj):以移动的方式在 queue 的尾部添加元素。这是通过调用底层容器的具有右值引用参数的成员函数 push_back() 来完成的。
- pop():删除 queue 中的第一个元素。
- size():返回 queue 中元素的个数。
- empty():如果 queue 中没有元素的话,返回 true。
- emplace():用传给 emplace() 的参数调用 T 的构造函数,在 queue 的尾部生成对象。
- swap(queue &other_q):将当前 queue 中的元素和参数 queue 中的元素交换。它们需要包含相同类型的元素。也可以调用全局函数模板 swap() 来完成同样的操作。
queue 模板定义了拷贝和移动版的 operator=(),对于所保存元素类型相同的 queue 对象,它们有一整套的比较运算符,这些运算符的工作方式和 stack 容器相同。
5、unordered_set 和unordered_map(c++11特性)
5.1 unordered_set
unordered_set可以把它想象成一个集合,它提供了几个函数让我们可以增删查:
unordered_set::insert
unordered_set::find
unordered_set::erase
这个unorder暗示着,这两个头文件中类的底层实现----Hash。 也是因为如此,你才可以在声明这些unordered模版类的时候,传入一个自定义的哈希函数,准确的说是哈希函数子(hash function object)。
单向迭代器
哈希表的实现复杂了该容器上的双向遍历,似乎没有一种合适的方法能够做到高效快速。 因此,unorder版本的map和set只提供前向迭代器(非unorder版本提供双向迭代器)。
首先要include这个unordered_set头文件。
然后就是第六行我们定义了一个整型int的集合,叫myset。
后面几行,我们演示了insert/find/erase的用法。
有两点需要注意:
一是这个容器是个集合,所以重复插入相同的值是没有效果的。大家可以看到我们这里第7行和第9行插入了2次3,实际上这个集合里也只有1个3,第10行输出的结果是2。
二是find的返回值是一个迭代器(iterator),如果找到了会返回指向目标元素的迭代器,没找到会返回end()。
5.2 unordered_map
unordered_map同样也提供了增删查函数:
unordered_map::insert
unordered_map::find
unordered_map::erase
这三个函数的平均时间复杂度也是O(1)的。我们可以看一个例子:
首先我们看第2行,要用unordered_map你要先include相应的头文件。
在7行我们定义了一个mymap,它的key是string类型,字符串;value是整形。
第8第9行展示的insert插入,因为我们这里要插入的是一个key/value pair(键值对),我们要用make_pair函数把一个字符串和一个整数打包成一个pair。
第10行是find查找,find返回的也是一个迭代器,iterator。这里我们懒得写很长的迭代器类型,直接用的auto。auto是c++11标准里的关键字,它会自动推断变量的类型。如果你的编译器不支持c++11,那你还是要老老实实写全:unordered_map<string, int>::iterator
迭代器指向的是一个pair,所以第11行我们可以用first和second去拿到对应的key和value,这里first是”c++”这个字符串,second是100这个整数。
第13第14行展示了一下erase删除。
值得一提的是,unordered_map重载了[]运算符,我们可以把key放在中括号里,像操作数组一样操作unordered_map:
上面这个程序的输出结果是101。大家可以看一下第8~第10行。我们把”c++”这个key放在中括号里就能直接操作”c++”对应的值。这种写法会让程序更直观。
6、基于范围的for循环(C++11)
对于一个有范围的集合如数组,本来就有范围,我们在使用循环而且确定循环范围就是多余的,C++11引入了范围for循环;语法是for循环后面括号内部被“:”分成两部分,前面一部分是auto& +迭代的变量,后一部分是迭代的范围;如对数组每个元素乘2并打印;
int arr[] = { 1, 2, 3, 4 };
for (auto& a : arr)
a *= 2;
for (auto& a : arr)
cout << a << " ";
7、c++ 栈 stack 用法
#include <stack>
stack<int>s1;
stack<string>s2;
empty() 堆栈为空则返回真
pop() 移除栈顶元素 (删除)
push() 在栈顶增加元素 (增加)
size() 返回栈中元素数目
top() 返回栈顶元素,不删除(获取)
8、c++ map
使用map得包含map类所在的头文件
#include <map> //注意,STL头文件没有扩展名.h
8.1 map的构造函数
map共提供了6个构造函数,这块涉及到内存分配器这些东西,略过不表,在下面我们将接触到一些map的构造方法,这里要说下的就是,我们通常用如下方法构造一个map:
map<int, string> mapStudent;
8.2 插入元素
// 定义一个map对象
map<int, string> mapStudent;
// 第一种 用insert函數插入pair
mapStudent.insert(pair<int, string>(000, "student_zero"));
// 第二种 用insert函数插入value_type数据
mapStudent.insert(map<int, string>::value_type(001, "student_one"));
// 第三种 用"array"方式插入
mapStudent[123] = "student_first";
mapStudent[456] = "student_second";
8.3 判断map中key值是否存在
1.find函数
iterator find ( const key_type& key );
如果key存在,则find返回key对应的迭代器,如果key不存在,则find返回尾后迭代器 .end()。可以参考下面的示例来判断key值是否存在
if (mymap.find(key) == mymap.end())
cout << "没有这个key" << endl;
2.count函数
count函数用于统计key值在map中出现的次数,map的key不允许重复,因此如果key存在返回1,不存在返回0
if (mymap.count(key) == 0)
cout << "no this key" << endl;
8.4 map元素的默认值
当map内元素值为int类型或常量时,默认值为0
当为String类型时,默认值不明,不显示。
9、c++优先队列(priority_queue)用法详解
头文件#include <queue>
优先队列具有队列的所有特性,包括基本操作,只是在这基础上添加了内部的一个排序,它本质是一个堆实现的
和队列基本操作相同:
top 访问队头元素
empty 队列是否为空
size 返回队列内元素个数
push 插入元素到队尾 (并排序)
emplace 原地构造一个元素并插入队列
pop 弹出队头元素
swap 交换内容
定义: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(),这个类就有了类似函数的行为,就是一个仿函数类了)
//对于基础类型 默认是大顶堆
priority_queue<int> a;
//等同于 priority_queue<int, vector<int>, less<int> > a;
priority_queue<int, vector<int>, greater<int> > c; //这样就是小顶堆
priority_queue<string> b;
10、C++ deque的用法与示例
deque 容器和 vector 容器最大的差异,一在于 deque 允许使用常数项时间对头端进行元素的插入和删除操作。二在于 deque 没有容量的概念,因为它是动态的以分段连续空间组合而成,随时可以增加一段新的空间并链接起来,换句话说,像 vector 那样,”旧空间不足而重新配置一块更大空间,然后复制元素,再释放旧空间”这样的事情在 deque 身上是不会发生的。也因此,deque 没有必须要提供所谓的空间保留(reserve)功能。
1.1deque构造函数
deque<T> deqT;//默认构造形式
deque(beg, end);//构造函数将[beg, end)区间中的元素拷贝给本身。
deque(n, elem);//构造函数将n个elem拷贝给本身。
deque(const deque &deq);//拷贝构造函数。
1.2 deque赋值操作
assign(beg, end);//将[beg, end)区间中的数据拷贝赋值给本身。
assign(n, elem);//将n个elem拷贝赋值给本身。
deque& operator=(const deque &deq); //重载等号操作符
swap(deq);// 将deq与本身的元素互换
1.3 deque大小操作
deque.size();//返回容器中元素的个数
deque.empty();//判断容器是否为空
deque.resize(num);//重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
deque.resize(num, elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置,如果容器变短,则末尾超出容器长度的元素被删除。
————————————————
版权声明:本文为优快云博主「JT同学」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/weixin_42462202/article/details/87537503
11、C++ pair的基本用法总结
1,pair的应用
pair是将2个数据组合成一组数据,当需要这样的需求时就可以使用pair,如stl中的map就是将key和value放在一起来保存。另一个应用是,当一个函数需要返回2个数据的时候,可以选择pair。 pair的实现是一个结构体,主要的两个成员变量是first second 因为是使用struct不是class,所以可以直接使用pair的成员变量。
其标准库类型–pair类型定义在#include 头文件中,定义如下:
类模板:template<class T1,class T2> struct pair
参数:T1是第一个值的数据类型,T2是第二个值的数据类型。
功能:pair将一对值(T1和T2)组合成一个值,
这一对值可以具有不同的数据类型(T1和T2),
两个值可以分别用pair的两个公有函数first和second访问。
定义(构造函数):
pair<T1, T2> p1; //创建一个空的pair对象(使用默认构造),它的两个元素分别是T1和T2类型,采用值初始化。
pair<T1, T2> p1(v1, v2); //创建一个pair对象,它的两个元素分别是T1和T2类型,其中first成员初始化为v1,second成员初始化为v2。
make_pair(v1, v2); // 以v1和v2的值创建一个新的pair对象,其元素类型分别是v1和v2的类型。
p1 < p2; // 两个pair对象间的小于运算,其定义遵循字典次序:如 p1.first < p2.first 或者 !(p2.first < p1.first) && (p1.second < p2.second) 则返回true。
p1 == p2; // 如果两个对象的first和second依次相等,则这两个对象相等;该运算使用元素的==操作符。
p1.first; // 返回对象p1中名为first的公有数据成员
p1.second; // 返回对象p1中名为second的公有数据成员
————————————————
版权声明:本文为优快云博主「sevencheng798」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/sevenjoin/article/details/81937695
12、c++中memset的用法
头文件:
#include<string.h>//C语言
#include<cstring>//c++
memset()函数原型是extern void *memset(void *buffer, int c, int count)
buffer:为指针或是数组
c:是赋给buffer的值
count:赋值buffer中的位数
1. memset是以字节为单位,初始化内存块。
当初始化一个字节单位的数组时,可以用memset把每个数组单元初始化成任何你想要的值,比如,
char data[10];
memset(data, 1, sizeof(data)); // right
memset(data, 0, sizeof(data)); // right
而在初始化其他基础类型时,则需要注意,比如,
int data[10];
memset(data, 0, sizeof(data)); // right
memset(data, -1, sizeof(data)); // right
memset(data, 1, sizeof(data)); // wrong, data[x] would be 0x0101 instead of 1
例如:
int dp[50][50][15][15];
memset(dp, -1, sizeof(dp));
//将dp数组所有值赋为-1
13、位运算
常用技巧
位运算是算法题里比较特殊的一种类型,它们利用二进制位运算的特性进行一些奇妙的优化和计算。常用的位运算符号包括:“∧”按位异或、“&”按位与、“|”按位或、“∼”取反、“<<”算术左移和“>>”算术右移。以下是一些常见的位运算特性,其中 0s 和 1s 分别表示只由 0 或 1
构成的二进制数字。
x ^ 0s = x x & 0s = 0 x | 0s = x
x ^ 1s = ~x x & 1s = x x | 1s = 1s
x ^ x = 0 x & x = x x | x = x
除此之外,n & (n - 1) 可以去除 n 的位级表示中最低的那一位,例如对于二进制表示 11110100,减去 1 得到 11110011,这两个数按位与得到 11110000。n & (-n) 可以得到 n 的位级表示中最低的那一位,例如对于二进制表示 11110100,取负得到 00001100,这两个数按位与得到 00000100。还有更多的并不常用的技巧,若读者感兴趣可以自行研究,这里不再赘述。
14、快速幂
快速幂算法的核心思想就是每一步都把指数分成两半,而相应的底数做平方运算。这样不仅能把非常大的指数给不断变小,所需要执行的循环次数也变小,而最后表示的结果却一直不会变。
让我们先来看一个简单的例子:
3^10=333333333*3
3^10=(33)(33)(33)(33)(3*3)
310=(3*3)5
310=95
95=(94)*(9^1)
95=(65611)*(9^1)
非递归版:
ll fastM(ll a, ll x, ll n) {
ll sum = 1;
while (x) {
if (x & 1)sum = sum * a % n;
a = a * a % n;
x >>= 1;
}
return sum;
}
三、常用技巧
1、上下左右
int direct[5] = {-1,0,1,0,-1};
for(int i = 0;i<4;++i){
x = row + direct[i];
y = col + direct[i+1];
}
2、log函数的一点小问题
为了避免log函数精度问题,在使用对数函数时,尽量使用*log10*
3、2 的(整数)次方
首先我们考虑一个数字是不是 2 的(整数)次方:如果一个数字 n 是 2 的整数次方,那么它
的二进制一定是 0…010…0 这样的形式;考虑到 n − 1 的二进制是 0…001…1,这两个数求按位与
的结果一定是 0。因此如果 n & (n - 1) 为 0,那么这个数是 2 的次方。
四、基本数学知识
1、 公倍数与公因数
1.1 欧几里得(辗转相除法)
利用辗转相除法,我们可以很方便地求得两个数的最大公因数(greatest common divisor,gcd);
将两个数相乘再除以最大公因数即可得到最小公倍数(least common multiple, lcm)。
int gcd(int a, int b) {
return b == 0 ? a : gcd(b, a% b);
}
int lcm(int a, int b) {
return a * b / gcd(a, b);
}
1.2 拓展欧几里得
进一步地,我们也可以通过扩展欧几里得算法(extended gcd)在求得 a 和 b 最大公因数的同
时,也得到它们的系数 x 和 y,从而使 ax + by = gcd(a, b)。
int xGCD(int a, int b, int &x, int &y) {
if (!b) {
x = 1, y = 0;
return a;
}
int x1, y1, gcd = xGCD(b, a % b, x1, y1);
x = y1, y = x1 - (a / b) * y1;
return gcd;
}
1.3 扩展欧几里得中求最小非负x的方法的推导
int D=b/gcd(a,b)
x%=D;
if(x<0){
x+=D;
}
//或者x=(x%D+D)%D
2、质数
质数又称素数,指的是指在大于 1 的自然数中,除了 1 和它本身以外不再有其他因数的自然
数。值得注意的是,每一个数都可以分解成质数的乘积。
2.1 分解质因数
for(int i=2; i<=m; i++)
{
while(m%i==0)
{
m=m/i;
cout<<i<<"*";
}
}
2.2 判断素数
bool prime(int n){
if(n < 2)return 0;
for(int i = 2;i<=sqrt(n);++i){
if(n % i == 0)return 0;
}
return 1;
}