随手笔记,仅供参考
C++中的数据结构
需要导入的头文件
#include <iostream>
#include <vector> // vector
#include <algorithm> // find, swap, sort, unique, reverse, lower_bound, min, max, max_element
#include <functional> // less, greater
#include <queue> // priority_queue, queue
#include <stack> // stack
#include <unordered_map> // unordered_map
#include <unordered_set> // unordered_set
#include <map> // map, multimap
#include <set> // set, multiset
#inlcude <utility> // pair, swap(exchange values of two objects)
#include <tuple> // tuple (get)
#include <string> // string
using namespace std;
两数之和
输入描述
输入数据有多组, 每行表示一组输入数据。
每行不定有n个整数,空格隔开。(1 <= n <= 100)。
输出描述
每组数据输出求和的结果
示例:
输入:
1 2 3
4 5
0 0 0 0 0
输出:
6
9
0
代码:
#include <iostream>
#include <vector>
using namespace std;
using ll = long long;
int solution(vector<int>& nums){
int n = nums.size();
ll sum = 0;
for(int i=0;i<n;i++){
sum += nums[i];
}
return sum;
}
void submit(int ans){
cout << ans << endl;
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
int n;
vector<int> nums;
while(cin >> n){
nums.push_back(n);
if(cin.get() == '\n'){
int ans = solution(nums);
submit(ans);
nums.clear();
}
}
return 0;
}
// 64 位输出请用 printf("%lld")
对输入的字符串进行排序
输入描述
多个测试用例,每个测试用例一行。
每行通过,隔开,有n个字符,n<100
输出描述
对于每组用例输出一行排序后的字符串,用','隔开,无结尾空格
示例
输入:
a,c,bb
f,dddd
nowcoder
输出:
a,bb,c
dddd,f
nowcoder
代码:
#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
using ll = long long;
vector<string> solution(vector<string>& nums){
int n = nums.size();
sort(nums.begin(), nums.end());
return nums;
}
void submit(vector<string>& ans){
int n = ans.size();
for(int i=0;i<n;i++){
cout << ans[i];
if(i != n-1){
cout << ",";
}
}
cout << endl;
}
void split(string& s, vector<string>& tokens, const string& delimiters=" "){
auto last_pos = s.find_first_not_of(delimiters, 0);
auto pos = s.find_first_of(delimiters, last_pos);
while(pos!=string::npos || last_pos !=string::npos){
tokens.push_back(s.substr(last_pos, pos-last_pos));
last_pos = s.find_first_not_of(delimiters, pos);
pos = s.find_first_of(delimiters, last_pos);
}
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
string a;
while(getline(cin, a)){
vector<string> nums;
split(a, nums, ",");
auto ans = solution(nums);
submit(ans);
}
return 0;
}
// 64 位输出请用 printf("%lld")
数组(向量)-- vector
#include <vector>
对象定义法
//一维vector
vector<int> *v = new vector<int>();
//二维vector
vector<vector<int>> *v2 = new vector<vector<int>*>();
遍历
//打印输出(二维数组)
for(int i=0;i<v2->size();i++){
for(int j=0;j<v2->at(0)->size();j++){
cout << v2->at(i)->at(j) << endl;
}
}
普通定义法
//声明R行C列的数组,赋初值为0
vector<vector<int>> flag(R, vector<int>(C, 0));
维度为(n,2)vector的声明
第一种:
vector<vector<int>> cp(n,vector<int>(2,0));
for(int i=0;i<n;i++){
cp[i][0] = capital[i];
cp[i][1] = profits[i];
}
第二种:
vector<vector<int>> cp(n);
for(int i=0;i<n;i++){
// cp[i].emplace_back(capital[i]);
// cp[i].emplace_back(profits[i]);
cp[i].push_back(capital[i]);
cp[i].push_back(profits[i]);
}
理论上emplace_back()
快一点。
在本例中,push_back()
稍微比emplace_back()
快一点。
第三种:
vector<pair<int,int>> cp;
for(int i=0;i<n;i++){
// cp[i] = {capital[i],profits[i]};
cp.push_back({
capital[i],profits[i]});
}
第三种方法最快,第二种次之,第一种最慢。
vector<pair<int,int>>
比vector<vector<int>>
快一倍。
vector操作
push_back
在尾部插入
void push_back(const T& x):向量尾部增加一个元素X
emplace_back
在尾部构造
push_back()
向容器尾部添加元素时,首先会创建这个元素,然后再将这个元素拷贝或者移动到容器中(如果是拷贝的话,事后会自行销毁先前创建的这个元素)
emplace_back()
在实现时,则是直接在容器尾部创建这个元素,省去了拷贝或移动元素的过程
pop_back
在尾部删除
void pop_back():删除向量中最后一个元素
back()获得尾部元素
v.back()
front()获得头部元素
v.front()
size元素个数
int size() const:返回向量中元素的个数
insert(i,x)在i前插入
iterator insert(iterator i,x)
A.insert(A.begin()+k,x)
插入–合并vector
//在A的后面插入
A.insert(A.end(),B,begin(),B.end());
删除–erase
iterator erase (iterator position);
iterator erase (iterator first, iterator last);
获得首元素引用front,尾元素引用back
reference front():返回首元素的引用
reference back():返回尾元素的引用
resize(n, value) 修改大小
修改容器大小,变大的部分用value修改为默认值
for (int i=1;i<10;i++) myvector.push_back(i);
myvector.resize(5);
myvector.resize(8,100);
myvector.resize(12);
//myvector包含:1 2 3 4 5 100 100 100 0 0 0 0
数组翻转
使用<algorthm>中的reverse()
#include <algorithm>
reverse(A.begin(),A.end())
复制vector
初始化构造时拷贝
vector<int> tem(list);
assign
vector<int> temlist;
temlist.assign(list.begin(), list.end());
Algorithm
unique
unique移除相邻的重复元素,如果想要移除所有(包括不相邻的)元素,必须先将序列排序,使所有重复元素相邻。
unique返回,最后一个被保留元素的下一个iterator
vector<int> myvector = {
1,2,2,2,3,3,2,2,1}
vector<int>::iterator it = unique(myvector.begin(),myvecotr.end())
#out:1,2,3,2,1,?,?,?,?
myvector.erase(it,myvector.end())
#out:1,2,3,2,1
sort排序
//升序
sort(A.begin(),A.end(),less<int>());
//降序
sort(A.begin(),A.end(),greater<int>());
自定义比较函数
【sort】 与 【堆/优先队列】生成的顺序刚好相反。
都是默认less<>()比较函数,但是【sort】之后元素升序排列;【堆/优先队列】生成大顶堆。
注:注意比较函数里面绝对不能写>=或者<=,比较函数要求如果两个元素相等,应返回false。你这里两个元素相等返回了true,导致stl的sort异常执行,导致段错误。
传入比较函数
struct Edge {
int len, x, y;
Edge(int len, int x, int y) : len(len), x(x), y(y) {
}
};
sort(edges.begin(), edges.end(), [](Edge a, Edge b) -> int {
return a.len < b.len; });
重载操作符
priority_queue的比较结果是【相反】的
class Point{
public:
int x;
int y;
int dist;
Point(int x,int y,int dist){
this->x = x;
this->y = y;
this->dist = dist;
}
//根据【希望】的情况进行返回
bool operator<(const Point &p) const{
return dist>p.dist;
}
};
priority_queue<Point> q;
max_element
vector<int> arr = {
1,2,3,4};
int maxNum = *max_element(arr.begin(),arr.end());
cout << maxNum << endl; // 4
队列 — queue
#include <queue>
queue<int> q;
队列操作
获得队首
q.front(); //获得队列最前面一个元素引用
获得队尾
q.back(); //返回队列最后一个元素引用
判空
q.empty(); //检查是否为空的方法
入队
q.push(); //在队列尾添加一个数据
出队
返回void
q.pop(); //删除队列头的一个数据,返回void
size()队列中元素个数
栈 —Stack
#include <stack>
stack<int> mystack;
栈操作
判空操作
s.empty(); //如果栈为空则返回true, 否则返回false;
获取大小
s.size(); //返回栈中元素的个数
获取栈顶元素
s.top(); //返回栈顶元素, 但不删除该元素
出栈
s.pop(); //弹出栈顶元素, 但不返回其值
进栈
s.push(); //将元素压入栈顶
s.emplace(); //构造并插入元素
堆
堆不是容器,而是组织容器元素的一种特别方式。
堆操作
创建堆
vector<int> A = {
};
//构造 小顶堆
mack_heap(A.begin(),A.end(),greater<>());
//构建 大顶堆(默认)
mack_heap(A.begin(),A.end(),less<>());
插入堆
A.push_back(x);
//greater/less必须与创建堆使用的相同
push_heap(A.begin(),A.end(),greater<>());
push_heap() 会因此认为最后一个元素是新元素,为了保持堆结构,会重新排列序列。
删除堆顶堆
pop_heap(A.begin(),A.end());
A.pop_back();
pop_head将第一个元素移动到最后
检查是否是堆
is_heap(A.begin(),A.end());
堆排序
//大顶堆
make_heap(A.begin(), A.end());//{12 10 3.5 6.5 8 2.5 1.5 6}
//升序排列
sort_heap(A.begin(), A.end());//{1.5 2.5 3.5 6 6.5 8 10 12}
//小顶堆
make_heap(A.begin(), A.end(),greater<>());// {1.5 6 2.5 6.5 8 12 3.5 10}
//降序排列
sort_heap(A.begin(), A.end(),greater<>());//{12 10 8 6.5 6 3.5 2.5 1.5}
优先队列
pair的比较,先比较第一个元素,第一个相等比较第二个。
//pair<x,y>键值对
priority_queue<pair<int, int>> q;
//降序队列,大顶堆,默认
priority_queue <int,vector<int>,less<int> >q;
//升序队列,小顶堆
priority_queue <int,vector<int>,greater<int> > q;
插入
//构造插入
q.emplace(dist, i);
push();
删除
q.pop(); //无返回值
获得顶部
q.top();
自定义比较函数
例1
匿名函数+decltype推断类型+构造方法
auto cmp = [](const pair<string, int>& a, const pair<string, int>& b) {
return a.second == b.second ? a.first < b.first : a.second > b.second;
};
priority_queue<pair<string, int>, vector<pair<string, int>>, decltype(cmp)> queue(cmp);
例2
结构体内比较函数+传入比较函数泛型
struct cmp {
bool operator ()(pair<string, int>& a, pair<string, int>& b) {
return a.second == b.second ? a.first < b.first : a.second > b.second;
}
};
priority_queue<pair<string, int>, vector<pair<string, int>>, cmp> queue;
哈希表
map升降序
map<int,int,less<int>> mmap; //默认升序
map<int,int,greater<int>> mmap; //降序
哈希表操作
#include <unordered_map>
unordered_map<string,int> map,
插入
map.insert (x); // 复制插入
map.insert (make_pair<string,double>("x",y)); // 移动插入
map.emplace(7, "456"); //构造插入
删除
map.erase(map.begin()+x); //通过位置
map.erase("x"); //通过key
清空
map.clear();
查找
if(map.find("x") != map.end()){
//查找成功
}else{
//查找失败
}
if (map.count("x")) {
//查找成功
}
int val = map["x"];
//直接使用key值访问键值对,如果没有访问到,返回0
遍历
map里面的数据类型是pair<T,P> p = {
key,value}
//first 是map的key
//second 是map的value
for (auto& [key,value]: map)
cout << key << " "<< value << endl;
for(auto it=map.begin();it!=map.end();it++){
}
集合
c++ std中set与unordered_set区别和map与unordered_map区别类似:
set基于红黑树实现,红黑树具有自动排序的功能,因此map内部所有的数据,在任何时候,都是有序的。
unordered_set基于哈希表,数据插入和查找的时间复杂度很低,几乎是常数时间,而代价是消耗比较多的内存,无自动排序功能。底层实现上,使用一个下标范围比较大的数组来存储元素,形成很多的桶,利用hash函数对key进行映射到不同区域进行保存。
unordered_set<int,int> set;
插入
emplace
insert
push_back()
append()
删除
erase
pop_back()
查找
count
find
遍历
for (auto& x: set)
cout << x << endl;
自定义set排序
Modern C++20
auto cmp = [](int a, int b) {
return ... };
set<int, decltype(cmp)> sset;
Modern C++11
lambda表示式需要传入构造函数。
auto cmp = [](int a, int b) {
return ... };
std::set<int, decltype(cmp)> sset(cmp);
Old solution使用struct的operator()
函数后面加const
表示常量函数(const member function)。
关于C++ 的constant Member function 的讲解
const member function
内不能修改成员变量(data member)。const object
只能调用const member function
。
struct cmp {
bool operator() (int a, int b) const {
return ...
}
};
set<int, cmp> sset;
for循环
for-each
//for-each
for(auto c:S){
ascii[c]++;
}
数组赋值memset
memset(A,0,sizeof(A));
键值对 pair
创建pair
//创建一个空的pair对象(使用默认构造),它的两个元素分别是T1和T2类型,采用值初始化。
pair<T1, T2> p1;
//创建一个pair对象,它的两个元素分别是T1和T2类型,其中first成员初始化为v1,second成员初始化为v2。
pair<T1, T2> p2(v1, v2);
新建pair
// 以v1和v2的值创建一个新的pair对象,其元素类型分别是v1和v2的类型。
pair<T1, T2> p3 = make_pair(v1, v2);
or
pair<T1, T2> p3 = {
v1,v2};
访问pair
// 返回对象p1中名为first的公有数据成员
p.first;
// 返回对象p1中名为second的公有数据成员
p.second;
例子
//二维数组
vector<vector<int>> m;
queue<pair<int, int>> q;
q.push({
r0, c0 });
//m[i][0] = r0,m[i][1]= c0
//或者说 插入一个[r0,c0] , 或者说 插入一个 一维数组
//此时二维数组元素 [ [r0,c0] ]
m.push_back({
r0, c0 });
pair<int, int> cur = q.front();
//pair访问
int x1 = cur.first + x[j];
int y1 = cur.second + y[j];
tuple 元组
tuple的使用
vector<tuple<int, int, int, int>> ans;
ans.emplace_back(steps, grid[i][j], i, j);
创建一个tuple
# 1.
tuple<int,flaot,vector<int>> t;
# 2.
tuple<string,vector<double>,int> someVal("tuple",{
2.14,3.15},100);
# 3.
tuple<int,int,double> someVal{
2,3,3.15};
访问tuple
auto item = make_tuple("string",3,20.01);
auto book = get<1>(item);
查询tuple成员数量
auto sz = tuple_size<decltype(item)>::value;
//sz = 3
查询tuple成员类型
tuple_element<1,decltype(item)>::type ctype;
//ctype的类型为int
string 字符串
string 末尾不用加上’\0’
string::npos 表示 字符串末尾
基本操作
string s;
1) s.empty(); // s为空串 返回true
2) s.size();s.length(); // 返回s中字符个数 类型应为:string::size_type
3) s[n]; // 从0开始相当于下标访问
4) s1+s2; // 把s1和s2连接成新串 返回新串
5) s1=s2; // 把s1替换为s2的副本
6) v1==v2; // 比较,相等返回true
7) `!=, <, <=, >, >=` 惯有操作 任何一个大写字母都小于任意的小写字母
8) s.push_back() //其他操作类似于vector
大小写转换
string str = 'Abc';
//转大写
transform(str.begin(), str.end(), str.begin(), toupper);
>>> str = 'ABC'
//转小写
transform(strA.begin(), strA.end(), strA.begin(), tolower);
substr求子串
pos:
Position of the first character to be copied as a substring.
If this is equal to the string length, the function returns an empty string.
If this is greater than the string length, it throws out_of_range.
Note: The first character is denoted by a value of 0 (not 1).
- 返回的是原字符串的copy。
- 从0开始数
- 如果pos刚好【等于】字符串长度,返回空串。
- 如果pos【大于】字符串长度,抛出out_of_rang异常。
len:
Number of characters to include in the substring (if the string is shorter, as many characters as possible are used).
A value of string::npos indicates all characters until the end of the string.
- 如果len的长度【大于】剩下子串的长度,就全取。
- string::npos表示 取剩下子串的全部。
size_t:
size_t is an unsigned integral type (the same as member type string::size_type).
- 无符号整数。
string substr(size_t pos,size_t len) const
//第一个参数是index,第二个参数是子串长度
stinrg str = s.substr(index,len);
string => const char*
string str = "Hello World";
const char *ch1 = str.c_str();
const char *ch2 = str.data();
字符删除 erase
c++98
sequence (1) string& erase (size_t pos = 0, size_t len = npos);
character (2) iterator erase (iterator p);
range (3) iterator erase (iterator first, iterator last);
字符操作
//替换字符
字符串分割
s为原字符串,tokens为分割后字符串(需要传入),delimiters为分割符
void split(const string& s, vector<string>& tokens, const string& delimiters = " "){
string::size_type lastPos = s.find_first_not_of(delimiters, 0);
string::size_type pos = s.find_first_of(delimiters, lastPos);
while (string::npos != pos || string::npos != lastPos) {
tokens.push_back(s.substr(lastPos, pos - lastPos));//use emplace_back after C++11
lastPos = s.find_first_not_of(delimiters, pos);
pos = s.find_first_of(delimiters, lastPos);
}
}
//示例:
string s = "a,b,c";
vector<string> tokens;
split(s,tokens,",");
for(auto &t:tokens){
cout << t << " ";
}
>>> a b c
stringstream分词(单个空格)
c++ 之 std::move 原理实现与用法总结
move()
它唯一的功能是将一个左值强制转化为右值引用,继而可以通过右值引用使用该值,以用于移动语义。
vector<string> uncommonFromSentences(string s1, string s2) {
unordered_map<string, int> freq;
//分割空格
auto insert = [&](const string& str){
stringstream ss(str); // ss << s;
string buffer;
while(ss >> buffer){
++freq[move(buffer)];
}
}
insert(s1);
insert(s2);
...
}
字符查找
//返回str在字符串中第一次出现的位置(从index开始查找),如果没找到则返回string::npos
//string
size_type find( const basic_string &str, size_type index=0 );
//c-string
size_type find( const char *str, size_type index=0 ); // 同上
//返回str在字符串中第一次出现的位置(从index开始查找,长度为length),如果没找到就返回string::npos
//buffer
size_type find( const char *str, size_type index, size_type length );
// 返回字符ch在字符串中第一次出现的位置(从index开始查找),如果没找到就返回string::npos
//character
size_type find( char ch, size_type index=0 );
字符串比较 compare
unsigned int val = a.compare(b);
//string (1)
int compare (const string& str) const;
//substrings (2)
int compare (size_t pos, size_t len, const string& str) const;
int compare (size_t pos, size_t len, const string& str,size_t subpos, size_t sublen) const;
//c-string (3)
int compare (const char* s) const;
int compare (size_t pos, size_t len, const char* s) const;
//buffer (4)
int compare (size_t pos, size_t len, const char* s, size_t n) const;
char* => int
const char *str1 = "3.14159";
int num1 = std::atoi(str1);
string => int, double, long, long long …
string s = "123";
int num1 = stoi(s);
double num2 = stod(s);
long num3 = stol(s);
long long num4 = stoll(s);
stod(string str,size_t idx)
str:
String类型对象。
idx:
size_t类型的指针。如果idx不为空,则
将idx设置为【数字字符串】之后的下一个字符。
所以,idx应该传指针类型,或者引用类型。
例子:
string orbits("365.24 29.53");
size_t sz; //unsigned int64
double earth = stod(orbits,&sz);
cout << sz << orbits[sz] << 1 << endl;
double moon = stod(orbits.substr(sz));
cout << "The moon completes " << (earth/moon) << " orbits per Earth year.\n";
out:
6 1
The moon completes 12.3684 orbits per Earth year.
int => string
int num = 123;
string str = to_string(num);
char=>string
char ch = 'a'
string a = *new string(1, ch);
字符流 stringstream
#include <sstream>
stringstream stream;
string sint="1";
string sfloat="1.1";
string sdouble="1.2";
int dint;
float dfloat;
double ddouble;
//string to int
stream << sint;
stream >> dint;
//string to float
stream.clear(); //注意需要清空对象
stream << sfloat;
stream >> dfloat;
//string to double
stream.clear(); //注意需要清空对象
stream << sdouble;
stream >> ddouble;
判断字符串是否为字母 isalpha
isalpha(s)
大小写字母,返回true,其他返回false。
匿名函数
C++11提供了对匿名函数的支持,称为Lambda函数(也叫Lambda表达式). Lambda表达式具体形式如下:
[capture](parameters)->return-type{body}
如果没有参数,空的圆括号()可以省略.
[capture]->return-type{body}
如果没有返回值,返回值也可以省略,
[capture](parameters){body}
//例子
[](int x, int y) -> int {
int z = x + y; return z; }
Lambda函数可以引用在它之外声明的变量. 这些变量的集合叫做一个闭包. 闭包被定义在Lambda表达式声明中的方括号[]内. 这个机制允许这些变量被按值或按引用捕获.下面这些例子就是:
[] //未定义变量.试图在Lambda内使用任何外部变量都是错误的.
[x, &y] //x 按值捕获, y 按引用捕获.
[&] //用到的任何外部变量都隐式按引用捕获
[=] //用到的任何外部变量都隐式按值捕获
[&, x] //x显式地按值捕获. 其它变量按引用捕获
[=, &z] //z按引用捕获. 其它变量按值捕获
decltype关键字
有时我们希望从表达式的类型推断出要定义的变量类型,但是不想用该表达式的值初始化变量(如果要初始化就用auto了)。为了满足这一需求,C++11新标准引入了decltype类型说明符,它的作用是选择并返回操作数的数据类型,在此过程中,编译器分析表达式并得到它的类型,却不实际计算表达式的值。
int getSize();
int main(void)
{
int tempA = 2;
/*1.dclTempA为int*/
decltype(tempA) dclTempA;
/*2.dclTempB为int,对于getSize根本没有定义,但是程序依旧正常,因为decltype只做分析,并不调用getSize,*/
decltype(getSize()) dclTempB;
return 0;
}
decltype和auto都可以用来推断类型,但是二者有几处明显的差异:
- auto忽略顶层const,decltype保留顶层const;
- 对引用操作,auto推断出原有类型,decltype推断出引用;
- 对解引用操作,auto推断出原有类型,decltype推断出引用;
- auto推断时会实际执行,decltype不会执行,只做分析。
总之在使用中过程中和const、引用和指针结合时需要特别小心。