文章目录
前言
# 易错语法
// c只能是int / char 不可以是string
switch(c){
case 1:xxx;break;
case 2:xxx;break;
default:break;
}
常用C++库函数
isalpha(char c) 字母字符
isdigit(char c) 数字字符
isallnum() 等价于 isalpha()||isdigit()
isupper(char c) 大写
islower(char c) 小写
toupper(char c)
tolower(char c)
__gcd(x, y) //最大公约数,最小公倍数=x*y/gcd(x,y)
字符串全变小写
transform(tmp.begin(),tmp.end(),tmp.begin(),::tolower); //大写 toupper
//手写
long gcd(long a, long b) [return b == 0 ? a : gcd(b, a % b);}
INT_MAX
INT_MIN
LONG_MAX
关于取模
在过程中取模,为防止溢出可以用long long,同时前面声明一个mod变量(以防玄学bug)
class Solution {
public:
int mod = 1e9 +7;
int maximumProduct(vector<int>& nums, int k) {
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
long long res;
while(!pq.empty()){
res = (res*pq.top()) % mod;
pq.pop();
}
return res;
}
};
STL排序
sort
有stl算法,也有stl容器带的成员函数。我一般习惯使用stl算法 即 sort(a.begin,a.end());
但根据《effective stl》同名情况的容器成员函数要比算法高效。
sort(a.begin(), a.end(), std::greater<>()); //降序
sort(a.begin(), a.end(), std::less<>()); //升序
partial_sort
排序前n个
bool cmp(int &a, int &b) {
return a > b;
}
partial_sort(nums.begin(),nums.begin()+k,nums.end(),cmp);
//把按照cmp的前k个放到前k个的位置
注意!第二个参数是开区间
nth_element
找出前n个(前n个本身无序)
与partial_sort参数一致
partition
保证i左边满足条件a,右边满足条件!a,类似快排的一次定基准。
数组
strlen(a)//数组长度
memset(a,0,sizeof(a));//初始化数组,只能初始化为0或-1
memset(a,1,sizeof(a));
strlen和sizeof的区别
1 strlen求的是字符串的长度 以\0结尾 sizeof求的是大小
char str[20] = "hello";
printf("strlen: %d\n", strlen(str));
printf("sizeof: %d\n", sizeof(str));
2 sizeof可以用类型或者函数作参数,sizeof只能用char*
pair
pair内部甚至可以用vector。 ex.pair<string,vector< int >>
stack
stack<int> mystack;
mystack.push(i); //入栈
mystack.pop(); //弹栈
mystack.top(); //返回栈顶值
mystack.size();
if(!mystack.empty()) //如果栈非空 即 若栈为空,s.empty()=1
注意:不能直接访问mystack[i],无自带迭代器,遍历需要自行设置循环
while(!mystack.empty)
{top();pop();}
or
int n=mystack.size();
for(int i=0;i<n;i++)
{top();pop();}
栈不好用双指针模拟
queue
queue<int> que;
que.push(i); //队尾入队
que.pop(); //队首出队
que.back(); //返回队尾
que.front(); //返回队首
!!这里要注意,所以一般都用front(),因为是队尾入队,队首出队。
empty() //是否为空
size() //队列大小
注意:不能直接访问queue[i], 且无自带迭代器
队列能用双指针模拟
deque
deque<int> dq;
dq.emplace_back(1);
dq.empace_front(2);
int back = dq.back();
int front = dq.front();
dq.pop_back();
dq.pop_front();
有迭代器
且可以通过dq[i]访问已有元素。
https://www.cplusplus.com/reference/deque/deque/operator[]/
priority_queue
关于遍历
和queue一样,没有迭代器
top 访问队头元素 此处与队列的front不同
empty 队列是否为空
size 返回队列内元素个数
push 插入元素到队尾 (并排序)
emplace 原地构造一个元素并插入队列
pop 弹出队头元素
swap 交换内容
在queue的基础上,让优先级高的排在了队列前面,队列中优先级max排在队首。本质上维护了一个堆。
//默认是大顶堆,即队头为队内最大值,(从队头到队尾)降序队列(less)
priority_queue<int,vector<int>,less<int> >que; //注意最后面>空格>
//小顶堆,即队头为队内最小值,升序队列(greater)
priority_queue<int,vector<int>,greater<int> >que;
//pair的情况
priority_queue<pair<int,int>,vector<pair<int,int>>,cmp> que;
三个参数的含义:1.队内每个元素的类型 2.队整体的类型:直接vector<1>即可 3.排序方法(可重载)
注意:其中“1”可以为相当多的类型,比如说为pair,tuple,甚至vector,这样就能利用上自带的排序。
cmp重载方法
1 单独struct重载cmp(): 用于set map 优先队列 <,,cmp> ex. set<int,cmp>
//cmp重载方法 1.放到结构体里 2.排序与sort正好相反(升序 return a>b;降序 return a<b)
struct cmp1{
bool operator ()(int a,int b){
return a>b;//最小值优先
}
};
struct cmp2{
bool operator ()(int a,int b){
return a<b;//最大值优先
}
};
struct cmp3{
bool operator (){pair<int,int> a, pair<int,int> b}{
return a.second<b.second; //第二个元素最大值优先
}
}
2 结构体内重载< :用于使用结构体的情况 --见fly刷题笔记
3 sort时重载cmp
bool cmp();
sort(a,a+n,cmp);
这里对重载sort方法的cmp又有了更深的理解。
bool cmp(int a, int b){
return expression;
}
expression为一个表达式
若expression为true,则顺序就按参数列表里的顺序不变,即先a后b
若expression为false,则先b后a
因此,return a<b是升序,反之是降序。
当然expression也可以不包含a和b,只要是表达式即可。
以上在传参的过程中使用值传递或者引用传递&均可
- 值传递 void x(int a):形参的变化不影响实参
- 指针传递 void x(int *a):传递地址,所以会影响外界
- 引用传递 void x(int &a):形参是实参的“小名”,形参的变化影响实参
string
截取子串substr(起始地址,长度)
string s="abcdefg";
s.substr(1,4) //"bcde"
不写第二个参数即一直到结尾
s.substr(2) //c->结尾
erase() 去除string中的某段字符
str.erase (str.begin()+1, str.end()-5); //去掉了bc
str.erase(1,2); //去掉了bc (这时的第二参数代表长度)
string to int
string s="12345";
int num = stoi(s);
//int num = atoi(s.c_str());
int to string
int x;
string s =to_string(x);
char to int
char c ='1';
int x=c-'0'
int to char
int c =1;
char x= c+48;
string ‘+’ 可以是char 也可以是string
string s="12345";
string s1="1";
char c = '1';
s=s+s1;
s=s+c; //这种可以,因为有一个s作为加法的一部分,两个c相加则不可以。
char to string
常用的如下:
string s;
char c = 'c';
s = c;
s.emplace_back(c);
s += c;
注意,以下的几种不可以!!!
string s = c;(必须分两步)
string s;
s = c + c;(先要构造出一个s在进行加法)
string to char
string s;
s[0],s[1]
string to char*
string s;
s.c_str();
寻找子串
find() //返回子串在主串中的起始位置
string str;
char c;
size_t pos = str.find(c,lastpos);
//从lastpos往后找的第一个c的位置
//用size_t是因为找不到了会返回string::npos
注意!
map的find找不到返回end()
string的find找不到返回string::npos
替换字串
replace() //找到子串,并替换为新的子串
string s;//主串
string lasts;//子串
string news;//新的子串
int i=s.find(lasts);//返回子串在主串中的起始位置
if(i!=std::string::npos)//找不到的情况
s=s.replace(s.find(lasts),lasts.size(),news);
//第一个参数,想要替换的起始位置,子串长度,新的子串
关于手写子串全部替换replace
void replace(string sub, string rep, string & s){
size_t pos = 0;
pos = s.find(sub);
while(pos!=string::npos){
s.replace(pos,sub.size(),rep);
pos = s.find(sub);
}
return ;
}
string a;
string b;
vector<int> ca(26),cb(26); //这个方法可以直接给vector设置大小,且初值为0
int n=a.size(),m=b.size();
for(char c:a) ca[c-'a']++; //string的迭代简化
for(char c:b) cb[c-'a']++;
(str1) int compare(pos,len,str2) //pos len是str1的
int ret = str1.compare(pos,len,str2);
ret = 0 -> =
ret > 0 -> str1 > str2
ret < 0 -> str1 < str2
关于 +=
!!!注 意 !!!
+=比 = x+要快很多!!!
https://blog.csdn.net/weixin_43222324/article/details/106814953
list
list构造函数
list<T> lstT;//list采用采用模板类实现,对象的默认构造形式:
list(beg,end);//构造函数将[beg, end)区间中的元素拷贝给本身。
list(n,elem);//构造函数将n个elem拷贝给本身。
list(const list &lst);//拷贝构造函数。
list数据元素插入和删除操作
push_back(elem);//在容器尾部加入一个元素
pop_back();//删除容器中最后一个元素
push_front(elem);//在容器开头插入一个元素
pop_front();//从容器开头移除第一个元素
insert(pos,elem);//在pos位置插elem元素的拷贝,返回新数据的位置。
insert(pos,n,elem);//在pos位置插入n个elem数据,无返回值。
insert(pos,beg,end);//在pos位置插入[beg,end)区间的数据,无返回值。
clear();//移除容器的所有数据
erase(beg,end);//删除[beg,end)区间的数据,返回下一个数据的位置。
erase(pos);//删除pos位置的数据,返回下一个数据的位置。
remove(elem);//删除容器中所有与elem值匹配的元素。
list大小操作
size();//返回容器中元素的个数
empty();//判断容器是否为空
resize(num);//重新指定容器的长度为num,
若容器变长,则以默认值填充新位置。
如果容器变短,则末尾超出容器长度的元素被删除。
resize(num, elem);//重新指定容器的长度为num,
若容器变长,则以elem值填充新位置。
如果容器变短,则末尾超出容器长度的元素被删除。
list赋值操作
assign(beg, end);//将[beg, end)区间中的数据拷贝赋值给本身。
assign(n, elem);//将n个elem拷贝赋值给本身。
list& operator=(const list &lst);//重载等号操作符
swap(lst);//将lst与本身的元素互换。
list数据的存取
front();//返回第一个元素。
back();//返回最后一个元素。
list反转排序
reverse();//反转链表,比如lst包含1,3,5元素,运行此方法后,lst就包含5,3,1元素。
sort(); //list排序 使用方式:l.sort();
list iterator的prev和next
prev() next()
实操使用细节
#include<bits/stdc++.h>
using namespace std;
list<int> l(5,0); //list的初始化,第一个参数代表长度,第二个参数代表每个节点的值
void preinsert(int i,int x) //在第i个节点前插入x (i从0到n-1)
{
if(l.size()==0&&i==0) l.push_back(x);
if(i>=l.size()) return; //错误指令
int times=i;
/*
insert和erase要注意迭代器失效的情况,通过这种遍历方式可以避免。
同时,这种处理的方式是万能的,map,hash都可以尝试用这种方式解决迭代器失效的问题
list自带的insert就是在该节点前插入新值
写break是因为这道题每次只操作一个点
*/
for(auto it=l.begin();it!=l.end();)
{
if(times==0)
{
it = l.insert(it,x);
break;
}
else ++it;
times--;
}
}
void nextinsert(int i,int x)//在第i个节点后插入x
{
if(l.size()<=i) return;
int times=i;
//list自带的insert是在该节点前插入新值
//因此,可以使用stl的next(),自动调用该节点的下一个,同时这种方法也不用担心最后的null,stl内部已经考虑到了
for(auto it=l.begin();it!=l.end();)
{
if(times==0)
{
auto p =next(it);
l.insert(p,x);
break;
}
else ++it;
times--;
}
}
void replacel(int i,int x)//把第i个节点的值替换为x
{
if(l.size()<=i) return;
int times=i;
for(auto it=l.begin();it!=l.end();)
{
if(times==0)
{
*it=x;
break;
}
else ++it;
times--;
}
/*
or
int times=i;
for(auto &it:l)
{
times--;
if(times==0)
{
it=s;
break;
}
}
*/
}
void erasel(int i)//把第i个节点去掉
{
if(l.size()<=i) return;
int times=i;
for(auto it=l.begin();it!=l.end();)
{
if(times==0)
{
it=l.erase(it);
break;
}
else ++it;
times--;
}
}
void print()//打印
{
for(auto elem:l) cout<<elem<<" ";
cout<<endl;
}
int main()
{
print();
preinsert(0,1);
print();
nextinsert(0,2);
print();
replacel(2,3);
print();
erasel(1);
print();
return 0;
/*
output:
0 0 0 0 0
1 0 0 0 0 0
1 2 0 0 0 0 0
1 2 3 0 0 0 0
1 3 0 0 0 0
*/
}
vector
当作数组来用即可
vector无法直接cout,需要遍历输出。
vector遍历不用迭代器也可,有时不用更方便
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
bool cmp(int a, int b)
{
return a>b;
}
int main()
{
//新建 输入 输出
vector<int>a;
vector<int>b;
for(int i=0;i<5;i++)
{
a.push_back(i);
// b[i]=i; ATTENTION! WRONG! 下标只能获取元素,不能新建
b.push_back(2*i);
cout<<"a"<<a[i]<<" ";
cout<<"b"<<b[i]<<" ";
}
cout<<endl;
//去掉容器最后的数据
for(int i=0;i<a.size();i++)
{
a.pop_back();
}
//排序
sort(b.begin(),b.end(),cmp);//默认升序排列,降序排列的改变
//迭代器使用
cout<<"after sorting"<<endl;
for(auto iter=b.begin();iter!=b.end();++iter)
{
//cout<<b[i]<<endl;
cout<<*iter<<" ";
}
cout<<endl;
reverse(b.begin(),b.end()); //容器的颠倒
cout<<"after reversing"<<endl;
//伪迭代器使用(加&后循环内部可以改变vector本身,无&时无法改变本身,只为使用)
for(auto &elem:b)
{
cout<<elem<<" ";
}
//清除容器所有数据
b.clear();
return 0;
}
reverse(res.begin(),res.end());//反转vector
关于去重:unique和resize连用,因为unique会把多余的元素放在后面
需要注意,使用前要先sort,因为unique只能消除相邻的重复元素
sort可以排序嵌套的vector,例如下面例子的res类型是vector<vector< int >>
swap交换vector内部的值
关于赋初值:vector不能用memset,数组可以 memset(a,0,sizeof(a))
用resize即可
resize(n,value); 第二个参数为vector元素的默认值,不写第二个参数默认为0
需要注意注意注意的是!!!!
需要注意注意注意的是!!!!resize不与memset等价,
它不会改变已有的值,所以可以先clear,再resize。
https://www.icode9.com/content-4-855235.html
初始化vector也可以这样:vector a(10);
默认开一个10空间且值为0的vector,很方便!
sort(res.begin(),res.end());//去重
res.resize(unique(res.begin(),res.end())-res.begin());
这里也可以
int n = unique(res.begin(),res.end())-res.begin();
实际上[0-n)的范围内是去重
swap(res[2],res[4]);
res.back();//res的最后一个元素 相当于栈顶
当vector作参数时,
参数改变外界:用引用 vector &a;
不改变:vector b;
地址(*a)一般不用,因为调用的时候需要(*a)[i],相比于其他两种直接a[i],这种比较麻烦
vector<vector<int> > input(2,vector<int>(5,1)); //注意初始化内部的vector<int>
for(auto elem:input)
{
for(auto x:elem)
{
cout<<x<<" ";
}
cout<<endl;
}
//对应输出
1 1 1 1 1
1 1 1 1 1
关于二维vector的初始化
vector<vector<int>> res(r, vector<int>(c, 0));
//r行 每行c列
当第二维度各个长度不确定等后续push_back时
res.resize(n);//即可
当第二维度的各个长度不一致且有确定值的时候
二维数组全局变量visit赋值 先构造一个一维 不能直接resize二维()
vector<bool> tmp(board[0].size(),false);
visit.resize(board.size(),tmp);
当第一维度不确定第二维度确定时
https://blog.csdn.net/u013040591/article/details/80329066?spm=1001.2101.3001.6650.1&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1.pc_relevant_paycolumn_v3&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7ECTRLIST%7ERate-1.pc_relevant_paycolumn_v3&utm_relevant_index=2
二维数组玄学报错 同一层次 不能既resize又push_back
//报错
//因为 pas已经初始化空间为5,后面还push_back
vector<vector<int>> pas(5);
pas.push_back(1);
vector<int> t;
t.push_back(0);
pas.push_back(t);
cout<<pas[1][0]<<endl;
//正确
vector<vector<int>> pas;
vector<int> t;
t.push_back(0);
pas.push_back(t);
pas.push_back(t);
cout<<pas[1][0]<<endl;
vector的insert
insert(iterator it,const T& x):向量中迭代器指向元素前增加一个元素x
insert(iterator it,int n,const T& x):向量中迭代器指向元素前增加n个相同的元素x
insert(iterator it,const_iterator first,const_iterator last):向量中迭代器指向元素前插入另一个相同类型向量的[first,last)间的数据
ex. vec.insert(vec.begin(),1);在vector[0]前面插入一个0
set
关于遍历
需要使用迭代器
相当于没有value的map
插入后自带排序,底层实现红黑树
插入查找删除的复杂度均为logn
set<int> mp;
mp.count(key) //查找元素有无
insert(key); //加入元素 自动去重
erase(key); //去掉元素
将set
vector<int> t{tmp.begin(), tmp.end()};
map
注意事项
- map内部实现了一个红黑树(红黑树是非严格平衡二叉搜索树)因此,对于map进行的查找,删除,添加等一系列的操作都相当于是对红黑树进行的操作。
- map的特性是,所有元素都会根据元素的键值自动排序。
- map所有的元素都是pair, 同时拥有实值和键值,pair的第一元素被视为键值,第二元素被视为实值,map不允许两个元素有相同的键值。
- 我们不可以通过map的迭代器改变map的键值, 因为map的键值关系到map元素的排列规则,任意改变map键值将会严重破坏map组织。如果想要修改元素的实值,那么是可以的。
若想去掉key值,首先要用迭代器(真正的迭代器) 之后调用 mp.erase(iter)
插入查找删除的复杂度均为log(n),遍历的复杂度是O(n)
for(auto iter=mp.begin();iter!=mp.end();++iter)
{
//cout<<iter->first;
//cout<<(*iter).first
mp.erase(iter);
}
map的优点如下:
1.有序性,这是map结构最大的优点,其元素的有序性在很多应用中都会简化很多的操作
2.红黑树,内部实现一个红黑书使得map的很多操作在log(n)的时间复杂度下就可以实现,因此效率非常的高
map的缺点如下:
1.空间占用率高,因为map内部实现了红黑树,虽然提高了运行效率,但是因为每一个节点都需要额外保存父节点、孩子节点和红/黑性质,使得每一个节点都占用大量的空间
map的适用处:对于那些有顺序要求的问题,用map会更高效一些
multimap
multimap和map的操作类似,唯一区别multimap键值可重复。同一个key可以有n个value
即,multimap可以一对多,map只能一对一
multimap如何find
hash
即, unordered_map
优点: 因为内部实现了哈希表O(1),因此其查找速度非常的快<查找key值> key----value
缺点: 哈希表的建立比较耗费时间
适用处:对于查找问题,unordered_map会更加高效一些,因此遇到查找问题,常会考虑一下用unordered_map
使用count,返回的是被查找元素的个数。如果有,返回1;否则,返回0。注意,hash中不存在相同元素,所以返回值只能是1或0。
使用find(key),返回的是被查找元素的位置,没有则返回map.end()。
关于auto的使用:auto 可以自动写好变量的类型,当使用迭代器时,一般类型名较长,所以采用auto。
find(key)
当使用find的时候,需要与迭代器的类型连用,所以一般前面+auto
count返回值是数,所以一般不需要,直接写在if里即可。
for(int i)可以完全替换迭代器,有的时候迭代器可能会简便一些。
Hash的O(1)指的是插入 删除 查找 修改的平均速度
其最差情况都是O(n)
其中的查找包括hash[i],count,find
key值顺序
hash的key值按插入时的顺序排列
上面的简要看看
重点是下面这个!<建立+count+遍历>
注:hash遍历见之前iter的文章
//建立hash
unordered_map<int,int> hashmap={{1,10},{2,20},{3,30}};
or
unordered_map<int,int> hashmap;
hashmap[1]=10;
hashmap[2]=20;
hashmap[3]=30;
//判断hash表中是否存在某key值
if(!hashmap.count(key)) //count返回key在map中出现的次数 hash只能是1
{不存在这个key值}
else
{存在这个key值}
//ATTENTION!!!!!!!
//一个可以优化的点
//ATTENTION!!!!!!!
//收集频次的时候不用使用count 进行mp[key]=1 和 mp[key]++的分类讨论
//直接 mp[key]++就好
//因为对于尚未出现的key,mp[key]本身就是将其初始化为mp[key]=0
https://en.cppreference.com/w/cpp/container/unordered_map/operator_at
// leetcode #1 两数之和 应用hash O(1)查找特性 查找target-x
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int,int>hashtable;
for(int i=0; i!=nums.size(); ++i)
{
auto it = hashtable.find(target-nums[i]); // 应用find
if(it!=hashtable.end())
{
return {it->second, i};
}
hashtable[nums[i]]=i;
}
return {};
}
};
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int,int> hashtable;
for (int i = 0; i != nums.size(); i++) {
if (hashtable.count(target - nums[i])) //应用count
{
return { hashtable[target - nums[i]], i };
}
hashtable[nums[i]] = i;
}
return {};
}
};
#######注意#######
hash(unordered_set/map) 的key值不能为vector,pair,结构体等。因为没有对应的hash函数(通过std::hash)
支持的key值有什么
可见,支持大部分基本类型,没有vector,没有数组,没有vector
有string,有vector< std::bool >
同理,unordered_set里也不能放这些。
如果想用vector,可以用set(map),有一些题这样做时间是可以的。
unordered_set
相当于没有value的hash
unordered_set<int> hash;
hash.count(key) //查找元素有无
insert(key); //加入元素 自动去重
erase(key); //去掉元素
关于vector,map,hash中的排序、查找
- sort()
只有vector支持sort,所以有时为了实现排序需要把map/hash导入到vector<pair<key,value>>里,之后再使用重写cmp的sort() - 对key值的排序
对于map,建立时自带对key值的从小到大排序
对于hash,对key值排序需要导入vector<pair<key,value>>再sort ,复杂度O(nlogn) - 对value的排序
map和hash都需要导入vector再sort,复杂度O(nlogn) - key值查找(count)/ 取值(mp[i])
对于map,复杂度O(logn)
对于hash,复杂度O(1)
关于vector,map,hash中的赋值
-
map的key值可以是pair,hash的key值如果是pair的话,需要重写hash。所以一般查表型的hash表(两个元素确定一个value),选择map来实现。
-
map&hash
首次赋值mp[i]时,可以直接mp[i]=1或者mp[i]++,之所以在一些统计次数问题中需要if else,是不确定mp[i]的初始值为0。if(!mp.count(xxx)) mp[xxx]=1; else mp[xxx]++
-
vector
vector<int> res(n); //该操作可以直接替代下面的操作,相当于开了个大小为n的vector,(初始值都为0),后续可以直接赋值.
vector<int> res(n,m);第二个参数为初始值
vector<int> ans(res); 也可以直接copy现有的vector
首次赋值时,只能push_back(),不能访问vec[i]进行操作。
第二次操作时,可以直接访问vec[i]进行。
同时,push_back(x)中的x类别不限,可以整体push_back
vector可以直接赋值
vector<int> a={1,2,3};
vector<int> s;
s=a;
但是二维vector赋值时需要注意不能直接访问vec[i]
vector<vector<int>> a;
vector<int> b={1,2,3};
a.push_back(b);//第一种方案
如果一定要直接首次访问a[i],需要先开空间,resize可加第二个参数,规定初值,不写默认为0
a.resize(10);//i:0-9
a[i]=b;
fill & memset
cin & getline
Cin接受到空格/换行/tab
getline(cin,str) 接收到换行
https://blog.csdn.net/weixin_41042404/article/details/80934191
位操作
注意:位操作
c++位操作
x >> y //x右移y位
x << y //x左移y位
同时,要注意!位运算操作符的优先级没有大于号小于号高。
因此,要加括号。
ex.if(((1<<i)&x)>0)
二进制枚举类型题 见leetcode周赛csdn总结
/*
ques:
颠倒给定的 32 位无符号整数的二进制位。
ex.
11111111111111111111111111111101
->
10111111111111111111111111111111
*/
class Solution {
public:
uint32_t reverseBits(uint32_t n) {
uint32_t res = 0;
for(int i = 0; i < 32; i++){ //不能是n!=0 因为要把前导0全颠倒到最后面
res <<= 1;
res += n & 1;
n >>= 1;
}
return res;
}
};
注意
& |^ 都是每一位都做运算,1 & n,因为1的前面位置都为0,所以只看最后一位就可以了。
最小生成树
进制转化
#include <iostream>
#include <cstdio>
#include <stdlib.h>
using namespace std;
int main()
{
int a;
printf("输入你想转化的十进制数字:");
scanf("%d",&a);
printf("它的八进制表示为 --> %o\n",a);
printf("它的十六进制表示为 --> %x\n",a);
printf("它的十进制表示为 --> %d\n",a);
char s[1000];
itoa(a,s,2);
printf("它的二进制表示为 --> %s\n",s);
itoa(a,s,4);
printf("它的四进制表示为 --> %s\n",s);
itoa(a,s,16);
printf("它的十六进制表示为 --> %s\n",s);
return 0;
}
结构体的建立
基础结构体
struct people
{
int age;
int height;
string name;
//people(int x,int y,string z):age(x),height(y),name(z){}
};
调用时需要创建结构体对象 people a; people a(1,2,"jeremy");
struct people
{
}a,b[10];
这种相当于创建了对象a,和结构体数组b
链表结构体
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
树结构体
/**
* Definition for binary tree
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
struct Student
{
}a;
bool cmp(Student &s1,Student &s2)
{
return s1.age<s2.age;
}
sort(a,a+n,cmp);
新建结构体对象(变量/指针)
TreeNode *node = new TreeNode(nums[mid],l,r);
//结构体指针要指向一块新的内存
TreeNode node = TreeNode(nums[mid],l,r);
//构造函数 赋值 给 结构体变量
结构体内重载小于号 见fly刷题笔记
用于更改set/priority_queue的自带排序
https://zhuanlan.zhihu.com/p/368510835
struct Node{
int key;
int value;
int freq;
int time;
//注意:如果写了带参数的构造函数,一定要把不带参数的也写上,因为重写后,默认构造函数会失效。
//这里貌似跟编译器有关,有的时候可以不用写这个!
Node():key(),value(),freq(),time(){}
Node(int a,int b,int c,int d):key(a),value(b),freq(c),time(d){}
//Node类的比较函数 定义在结构体中
//这两处一定要写const 不然编译不过
bool operator < (const Node &a) const{
return freq == a.freq ? time < a.time : freq < a.freq; //从大到小 逻辑见fly刷题笔记
}
};
emplace和结构体/其他stl的丝滑操作
https://www.jianshu.com/p/3906f24d2697
要注意的点:
Node(int a,int b,int c,int d):key(a),value(b),freq©,time(d){}
如果要写这样的构造函数
一定要写 Node():key(),value(),freq(),time(){}
因为重写后 默认的就会失败。
struct Node{
int key;
int value;
int freq;
int time;
//注意:如果写了带参数的构造函数,一定要把不带参数的也写上,因为重写后,默认构造函数会失效。
//这里貌似跟编译器有关,有的时候可以不用写上面这个!
Node():key(),value(),freq(),time(){}
Node(int a,int b,int c,int d):key(a),value(b),freq(c),time(d){}
//Node类的比较函数 定义在结构体中
//这两处一定要写const 不然编译不过
bool operator < (const Node &a) const{
return freq == a.freq ? time < a.time : freq < a.freq; //从大到小 逻辑见fly刷题笔记
}
};
set<Node> st;
st.emplace(1,2,3,4);//直接可以传给构造函数 太丝滑了
不仅仅是结构体,参数列表是其他stl也可以,有待后续继续发现,不过struct是最常用的。
ex.pair->看这个题解的用法。
https://leetcode-cn.com/problems/all-oone-data-structure/solution/quan-o1-de-shu-ju-jie-gou-by-leetcode-so-7gdv/
数学操作
幂
z=pow(x,y); //x的y次幂
其中:z,x是double类型,y不要求类型
细节操作
int a,b,c=0;
//这种情况只初始化了c;a和b没有进行初始化
int a=0,b=0,c=0;
//这种情况才能将a b c全部都初始化
emplace
替换后可以使用语法糖。
例如emplace一个pair可以不用make_pair,emplace结构体是可以直接直接放置参数等。
https://blog.csdn.net/windpenguin/article/details/75581552
效率更高 尽量替换
总结相关语法如下
vector
emplace <-> insert
emplace_back <-> push_back
set
emplcace <-> insert
map
emplace <-> insert
而且emplace很丝滑 若emplace的是结构体对象,可以直接识别参数列表
详见 “emplace和结构体丝滑操作”
auto
auto [steps, i, j] = q.front();
tuple
https://blog.csdn.net/sevenjoin/article/details/88420885
可以用tuple来替代结构体
lambda
https://km.woa.com/group/46659/articles/show/489743?kmref=search&from_page=1&no=1
https://www.jianshu.com/p/932812748138
关于基本类型的初始化
int等需要初始化
string str; 自动初始化为空字符串
读写文件
读
ifstream ifs;
ifs.open("test.txt");
//按行读
string buf;
while(getline(ifs,buf)){
cout<<buf<<endl; //each line
}
写
ofstream ofs;
ofs.open("output.txt"); //没有就会新增
//按行读
stirng s1,s2;
ofs << s1 << endl;
ofs << s2 << endl;
ofs.close();
复杂case
#include <fstream>
#include <sstream>
void FlowLogParser::LoadLookUpTable(const string lookUpFile) {
ifstream file(lookUpFile);
if (!file.is_open()) {
cout << "Error opening lookup table file!" << endl;
exit(1);
}
string line;
while (getline(file, line)) {
if (line.empty()) continue;
stringstream ss(line);
string dstPortStr, protocol, tag;
getline(ss, dstPortStr, ',');
getline(ss, protocol, ',');
getline(ss, tag, ',');
lookUpTable[dstPortStr+"_"+protocol] = tag;
}
file.close();
}
void FlowLogParser::Parse(const string flowLogFile) {
ifstream file(flowLogFile);
if (!file.is_open()) {
cout << "Error opening flow log file!" << endl;
exit(1);
}
string line;
while (getline(file, line)) {
if (line.empty()) continue;
stringstream ss(line);
int protocolNumber;
string _tmp, dstPort;
// parse flow log
ss >> _tmp >> _tmp >> _tmp >> _tmp >> _tmp >> dstPort >> _tmp >> protocolNumber;
// match tag
string protocol = MapProtocolName(protocolNumber);
string key = dstPort + _tmp;
// no match tags
if (!lookUpTable.count(key)) {
portProtocolMatchCount[key]++;
} else {
string tag = lookUpTable[key];
tagMatchCount[tag]++;
portProtocolMatchCount[key]++;
}
}
file.close();
}
enum
// direction enum
enum Direction {
SOUTH = 0,
EAST = 1,
NORTH = 2,
WEST = 3
};
Direction cur_dir = SOUTH; // current direction
vector<Direction> moves; // store Optimus' moves