@thousfeet
2020-01-11T08:17:37.000000Z
字数 12923
阅读 883
LeetCode
(include < fstream > )
ifstream ifile;
ifile.open("xx.in");
double N;
ifile >> N;
ifile.close();
ofstream ofile;
ofile.open("xx.out");
ofile << N << endl;
ofile.close();
C++(include < iomanip > )
cout << N << endl; // 10.468565425
cout << setprecision(4) << N << endl; //4精度浮点数 10.47
cout << fixed << setprecision(4) << N << endl; //精确到小数点后4位 10.4686
cout << setw(4) << setfill('0') << int(N) << endl; //前补0至占4位
C
printf("%.4f\n", N); //精确到小数点后4位
printf("%04d\n", (int)N); //前补0至占4位
stoi
(include < string >)
//默认按照十进制转换
string str = "10086";
int a = stoi(str);
string str = "0.1111";
float b = stof(str);
//其他进制
std::string str_hex = "40c3";
std::string str_bin = "-10010110001";
std::string str_auto = "0x7f";
int i_hex = std::stoi (str_hex,nullptr,16);
int i_bin = std::stoi (str_bin,nullptr,2);
int i_auto = std::stoi (str_auto,nullptr,0);
to_string
string str = to_string(a);
(include < sstream >)
(直接转化时默认6位小数 如需固定精度则 ss << fixed << setprecision(8) << i;
#include <sstream>
#Include <string>
string num2str(double i)
{
stringstream ss;
ss<<i;
return ss.str();
}
int str2num(string s)
{
int num;
stringstream ss(s);
ss>>num;
return num;
}
要同一个ss复用的话记得清空:ss.str("");
上面方法很简便, 缺点是处理大量数据转换速度较慢,用下面的c的库函数会更快
可以用sprintf函数将数字输出到一个字符缓冲区中. 从而进行了转换(头文件< string > )
例如:
已知从0点开始的秒数(seconds) ,计算出字符串"H:M:S", 其中H是小时, M=分钟,S=秒
int H, M, S;
string time_str;
H=seconds/3600;
M=(seconds%3600)/60;
S=(seconds%3600)%60;
char ctime[10];
sprintf(ctime, "%d:%d:%d", H, M, S); // 将整数转换成字符串
time_str=ctime; // 结果
与sprintf对应的是sscanf函数, 可以将字符串转换成数字
char str[] = "15.455";
int i;
float fp;
sscanf( str, "%d", &i ); // 将字符串转换成整数 i = 15
sscanf( str, "%f", &fp ); // 将字符串转换成浮点数 fp = 15.455000
printf( "Integer: = %d ", i);
printf( "Real: = %f ", fp);
return 0;
(include < string > )
删去字符串中的一段。一般用的参数是 (位置,删去的长度) 或是(起始位置,结束位置)
std::string str ("This is an example sentence.");
std::cout << str << '\n';
// "This is an example sentence."
str.erase (10,8); // ^^^^^^^^
std::cout << str << '\n';
// "This is an sentence."
str.erase (str.begin()+9); // ^
std::cout << str << '\n';
// "This is a sentence."
str.erase (str.begin()+5, str.end()-9); // ^^^^^
std::cout << str << '\n';
// "This sentence."
(include < string > )
插入一串字符串。一般用的参数是(位置,插入的字符串)
std::string str="to be question";
std::string str2="the ";
std::string str3="or not to be";
std::string::iterator it;
// used in the same order as described above:
str.insert(6,str2); // to be (the )question
str.insert(6,str3,3,4); // to be (not )the question
str.insert(10,"that is cool",8); // to be not (that is )the question
str.insert(10,"to be "); // to be not (to be )that is the question
str.insert(15,1,':'); // to be not to be(:) that is the question
it = str.insert(str.begin()+5,','); // to be(,) not to be: that is the question
str.insert (str.end(),3,'.'); // to be, not to be: that is the question(...)
str.insert (it+2,str3.begin(),str3.begin()+3); // (or )
std::cout << str << '\n';
(include < string > )
取一段子串。一般用的参数是(起始位置,要取的长度),如果要从某个位置开始一直取到末尾则是单一参数(起始位置)
std::string str="We think in generalities, but we live in details.";
std::string str2 = str.substr (3,5); // "think"
std::size_t pos = str.find("live"); // position of "live" in str
std::string str3 = str.substr (pos); // get from "live" to the end
std::cout << str2 << ' ' << str3 << '\n';
(include < string > )
判断两字符串相等。若相等则返回0。
一般的用法即为str1.compare(str2)
,若要将子串和str2比对则参数是(子串起始位置,长度,str2),若是两字符串的子串比对则是(str1子串起始,str1子串长度,str2,str2子串起始,str2子串长度)
std::string str1 ("green apple");
std::string str2 ("red apple");
if (str1.compare(str2) != 0)
std::cout << str1 << " is not " << str2 << '\n';
if (str1.compare(6,5,"apple") == 0)
std::cout << "still, " << str1 << " is an apple\n";
if (str1.compare(6,5,str2,4,5) == 0)
std::cout << "therefore, both are apples\n";
(include < string > )
读入一行字符串。
string s;
getline(cin,s);
默认是换行为分隔符。
也可以用其他字符作为分隔符,如用空格作为分隔符。
string s;
getline(cin,s,' ');
(include < string > )
在字符串str的后面追加字符串。常用的参数是(追加的字符串)也即整串追加,或只追加一段,则是(追加的字符串,起始位置,长度)
std::string str;
std::string str2="Writing ";
std::string str3="print 10 and then 5 more";
str.append(str2); // "Writing "
str.append(str3,6,3); // "10 "
str.append("dots are cool",5); // "dots "
str.append("here: "); // "here: "
str.append(10u,'.'); // ".........."
str.append(str3.begin()+8,str3.end()); // " and then 5 more"
str.append<int>(5,0x2E); // "....."
(include < cstdio > )
从一个字符串中读进与指定格式相符的数据。
经常碰到一串固定格式字符串中有需要处理的数据,比如 0:23:21
-:–:–
:
sscanf(s.c_str(),"%d:%d:%d",&hh,&mm,&ss);
(include < cstdio > )
把格式化的数据写入某个字符串。
int num;
char s[1000];
sprintf(s,"%d",num);
(include < string > )
翻转字符串
reverse(s.begin(),s.end());
(include < algorithm>)
比当前char型数组排序大的下一种排序
next_permutation(c,c+strlen(c));
经过上面那条语句后,c[]=”abcd”变为 abdc
(include < cstring > )
对于对int之类的数组,只能用memset对其初始化为0或-1。
如:
int a[]; memset(a,0,sizeof(a));
而对于char型,可以赋任何字符。
如:
char a[]; memset(a,'0',sizeof(a));
(include < iostream > )
把那一块单元赋成指定的值
char s[100];
fill(s,s+100,'a');
如果用memset给d[100]赋值1会出事 这时候就应用fill函数
int d[100];
fill(d,d+100,1);
(include < string > )
找到则返回第一个字符的索引
没找到则返回 string::npos
(或者判一下int found > -1)
参数是:(待查字符串,被查字符串的起始位置,代查字符串的前几位)
std::string str ("There are two needles in this haystack with needles.");
std::string str2 ("needle");
// different member versions of find in the same order as above:
std::size_t found = str.find(str2);
if (found!=std::string::npos)
std::cout << "first 'needle' found at: " << found << '\n';
found=str.find("needles are small",found+1,6);
if (found!=std::string::npos)
std::cout << "second 'needle' found at: " << found << '\n';
found=str.find("haystack");
if (found!=std::string::npos)
std::cout << "'haystack' also found at: " << found << '\n';
found=str.find('.');
if (found!=std::string::npos)
std::cout << "Period found at: " << found << '\n';
// let's replace the first needle:
str.replace(str.find(str2),str2.length(),"preposition");
std::cout << str << '\n';
string s转char c[]
strcpy(c,s.c_str());
char c[] 转 string s
s = c;
1.声明和初始化
vector<int> vec; //声明一个int型向量
vector<int> vec(5); //声明一个初始大小为5的int向量
vector<int> vec(10, 1); //声明一个初始大小为10且值都是1的向量
vector<int> vec(tmp); //声明并用tmp向量初始化vec向量
vector<int> tmp(vec.begin(), vec.begin() + 3); //用向量vec的第0个到第2个值初始化tmp
int arr[5] = {1, 2, 3, 4, 5};
vector<int> vec(arr, arr + 5); //将arr数组的元素用于初始化vec向量,注意:其中不包括arr[5]元素,末尾指针都是指结束元素的下一个元素,
二维向量初始化为0
vector<vector<int>>vec(m,vector<int>(n,0));
2.基本操作
(1). 容量
向量大小: vec.size();
向量最大容量: vec.max_size();
更改向量大小: vec.resize();
向量真实大小: vec.capacity();
向量判空: vec.empty();
减少向量大小到满足元素所占存储空间的大小: vec.shrink_to_fit(); //shrink_to_fit
(2). 修改
多个元素赋值: vec.assign(); //类似于初始化时用数组进行赋值
末尾添加元素: vec.push_back();
末尾删除元素: vec.pop_back();
任意位置插入元素: vec.insert();//v1.insert(v1.end(), v2.begin(), v2.end()); /把v2加到v1末尾
任意位置删除元素: vec.erase(); //vec.erase (vec.begin()+5); vec.erase (vec.begin(),vec.begin()+3);
交换两个向量的元素: vec.swap();
清空向量元素: vec.clear();
(3)迭代器
开始指针:vec.begin();
末尾指针:vec.end(); //指向最后一个元素的下一个位置
指向常量的开始指针: vec.cbegin(); //意思就是不能通过这个指针来修改所指的内容,但还是可以通过其他方式修改的,而且指针也是可以移动的。
指向常量的末尾指针: vec.cend();
(4)元素的访问
下标访问: vec[1]; //并不会检查是否越界
at方法访问: vec.at(1); //以上两者的区别就是at会检查是否越界,是则抛出out of range异常
访问第一个元素: vec.front();
访问最后一个元素: vec.back();
返回一个指针: int* p = vec.data(); //可行的原因在于vector在内存中就是一个连续存储的数组,所以可以返回一个指针指向这个数组。这是是C++11的特性。
遍历
vector<int>::iterator it;
for (it = vec.begin(); it != vec.end(); it++)
cout << *it << endl;
//或者
for (size_t i = 0; i < vec.size(); i++) {
cout << vec.at(i) << endl;
}
翻转
#include <algorithm>
reverse(vec.begin(), vec.end());
排序
#include <algorithm>
sort(vec.begin(), vec.end()); //从小到大排序
sort(vec.begin(),vec.end(), greater<int>() ); //从大到小排序
//或采用下面方法:
bool Comp(const int& a, const int& b) {
return a > b;
}
sort(vec.begin(), vec.end(), Comp);
查找元素
if(find(v.begin(), v.end(), val) != v.end()){...} //val为待查找元素
//用如下方法返回待查元素出现的下标
find(v.begin(), v.end(), val)-v.begin();
求和
accumulate() 前两个参数指定要累加的元素范围,第三个是累加的初值。容器内的元素类型必须与第三个实参的类型匹配,或者可转换为第三个实参的类型
int sum1 = accumulate(vec.begin(),vec.end(),0);
double sum2 = accumulate(vec.begin(),vec.end(),0.0);
string sum3 = accumulate(v.begin() , v.end() , string(" "));//从空字符串开始,每个元素连接成一个字符串。
next_permutation
用来计算全排列的函数,如果存在下一个greater permutation则返回true,否则返回false。
next_permutation(nums.begin(), nums.end())
声明双端队列
deque<int> que;
使用
que.push_front();
que.push_back();
que.pop_front();
que.pop_back()
priority_queue <int> i; //默认从大到小
priority_queue <int,vector<int>,greater<int> > q; //从大到小
priority_queue <int,vector<int>,less<int> >q; //从小到大
队列中的也可以是结构体,需要重载‘<’符号
比如:
struct node
{
int x,y;
bool operator < (const node & a) const
{
return x<a.x;
}
}k;
priority_queue <node> q; //x从大到小排序
不重载 < 的办法:
struct node
{
int fir,sec;
}input;
struct cmp
{
bool operator () (const node &x,const node &y) const
{
return x.fir<y.fir;
}
};
priority_queue<node,vector<node>,cmp> q; //按fir值从大到小
q.size();//返回q里元素个数
q.empty();//返回q是否为空,空则返回1,否则返回0
q.push(k);//在q的末尾插入k
q.pop();//删掉q的第一个元素
q.top();//返回q的第一个元素
mapStudent.insert(pair<int, string>(1, "student_one")); //方式1
mapStudent[1] = "student_one"; //方式2
mapStudent.erase(1);
int nSize = mapStudent.size();
正向迭代器
map<int, string>::iterator it;
for(it = ma.begin(); it != ma.end(); it++)
cout<<it->first<<" "<<it->second<<endl;
反向迭代器
map<int, string>::reverse_iterator it;
for(it = ma.rbegin(); it != ma.rend(); it++)
cout<<it->first<<" "<<it->second<<endl;
数组形式
int nSize = ma.size();
//注意,是 for(int nindex = 1; nindex <= nSize; nindex++)
// 而不是 for(int nindex = 0; nindex < nSize; nindex++)
for(int nindex = 1; nindex <= nSize; nindex++)
cout<<ma[nindex]<<endl;
}
第一种:用count函数来判定关键字是否出现,其缺点是无法定位数据出现位置,由于map的特性,一对一的映射关系,就决定了count函数的返回值只有两个,要么是0,要么是1,出现的情况返回1
第二种:用find函数来定位数据(key)出现位置,它返回的一个迭代器,当数据出现时,它返回数据所在位置的迭代器,如果map中没有要查找的数据,它返回的迭代器等于end函数返回的迭代器。
map<int, string>::iterator iter;
iter = mapStudent.find(1);
if(iter != mapStudent.end())
cout<<"Find, the value is "<<iter->second<<endl;
else
cout<<"Do not Find"<<endl;
第三种:
lower_bound函数,用来返回要查找关键字的下界(是一个迭代器)
upper_bound函数,用来返回要查找关键字的上界(是一个迭代器)
例如:map中已经插入了1,2,3,4的话,如果lower_bound(2)的话,返回的2,而upper-bound(2)的话,返回的就是3
Equal_range函数返回一个pair,pair里面第一个变量是Lower_bound返回的迭代器,pair里面第二个迭代器是Upper_bound返回的迭代器,如果这两个迭代器相等的话,则说明map中不出现这个关键字,
pair<map<int, string>::iterator, map<int, string>::iterator> mappair;
mappair = mapStudent.equal_range(2);
if(mappair.first == mappair.second)
cout<<"Do not Find"<<endl;
else
cout<<"Find"<<endl;
(转换为vector然后排序)
struct cmp
{
bool operator()(const pair<int,int>& l, const pair<int,int>& r) {
return l.second > r.second;
}
};
vector<pair<int,int>> vec(ma.begin(), ma.end());
sort(vec.begin(), vec.end(), cmp());
std::map对应的数据结构是红黑树。红黑树是一种近似于平衡的二叉查找树,里面的数据是有序的。在红黑树上做查找、插入、删除操作的时间复杂度为O(logN)。而std::unordered_map对应哈希表,哈希表的特点就是查找效率高,时间复杂度为常数级别O(1), 而额外空间复杂度则要高出许多。所以对于需要高效率查询的情况,使用std::unordered_map容器,但是std::unordered_map对于迭代器遍历效率并不高。而如果对内存大小比较敏感或者数据存储要求有序的话,则可以用std::map容器。
用法是和map一样的。
insert() ,插入元素
begin() ,返回set容器的第一个元素
end() ,返回set容器的最后一个元素
clear() ,删除set容器中的所有的元素
empty() ,判断set容器是否为空
max_size() ,返回set容器可能包含的元素最大个数
size() ,返回当前set容器中的元素个数
rbegin ,返回的值和end()相同
rend() ,返回的值和begin()相同
结构体set的遍历
struct A
{
int age;
string name;
};
for (set<A>::iterator it = setA.begin(); it != setA.end(); it++)
{
A a = (A)(*it);
printf("age = %d, name = %s \n", a.age, a.name.c_str());
}
https://www.cnblogs.com/ECJTUACM-873284962/p/6905416.html
struct Edge
{
int to;
int val;
}edge[2*MAX_M];
void addEdge(int u ,int v, int w)
{
edge[++tot].to = v;
edge[tot].val = w;
next[tot] = head[u];
head[u] = tot;
}
struct UF
{
int fa[MAX_N];
void init() { for(int i = 1; i < MAX_N; i++) fa[i] = i;}
int find(int a) { return fa[a] == a ? a : fa[a] = find(fa[a]);}
void mix(int a,int b) { fa[find(a)] = find(b);}
bool isSame(int a, int b) { return find(a) == find(b);}
} uf;
图中所有边的权值都不同时,最小生成树唯一
如果有2条或以上的边有相同权值,最小生成树不一定唯一(但最小的权值和唯一)
考虑n结点m条边的图中的最小生成树算法:
Prim算法(选点)
设集合V为已选结点集合,集合U为未选结点集合。
一开始所有结点的dist都为INF,起始结点的dist更新为0。每次从U中选一个dist最小的结点 i 加入V中,更新 i 结点所能到达全部结点 j 的dist值为dist[i]加上i,j间的边权。这样直到所有结点都加入V中为止。
时间复杂度O(n*n),主要与结点数有关,适用于稠密图。
【示例代码】
dis存的是结点到已被选中的这些点的最短路径
void Prim() {
int i;
memset(vis,false,sizeof(vis));
memset(dis,0x3f,sizeof(dis));
cnt=dis[1]=0;
while(true) {
//寻找dis最小的结点u
u=0;
for(i=1;i<=n;++i)
if(!vis[i]&&dis[i]<dis[u])
u=i;
if(u==0)
return ;
//将u加入集合V
vis[u]=true;
++cnt;
//用u更新所有能到达的结点的dis
for(i=1;i<=n;++i)
dis[i]=min(dis[i],g[u][i]);
}
}
Kruskal算法(选边)
设集合E为已选的边集合,集合U为未选的边集合。
把所有边的边权val排序后,每次选一条最短的边(即遍历边),判断下这条边的两个结点是否已经相连(并查集维护结点间是否相连),若没有则加入集合E。执行n-1次的加入操作后退出。(生成树的边数为n-1)
时间复杂度O(mlog(m)),主要与边数有关,不适用于稠密图。
void kruskal(int num)
{
int ned = num-1;
uf.init();
for(int i = 0; i < top && ned; i++)
{
if(!uf.isSame(e[i].x,e[i].y))
{
uf.mix(e[i].x,e[i].y);
ned--;
}
}
SPFA算法
初始时所有点dist为INF,只有起始点的dist为0。然后建立一个队列,初始时队列里只有起始点,之后不断弹出队首结点 i 去更新它能到达的所有点 j 的dist值,如果dist[j] > dist[i]+value[i][j]则更新成功,再判断下被更新的结点若不在队列中(vis=0?),则把该点加入到队列。这样重复执行直到队列为空。
void spfa() //源点到结点距离dist
{
queue<int> que;
que.push(s);
vis[s] = true;
while(!que.empty())
{
int u = que.front();
for(j = head[u]; j ; j = next[j])
{
int v = edge[j].to;
if(dist[u]+edge[j].val < dist[v])
{
dist[v] = dist[u]+edge[j].val;
if(!vis[v])
{
que.push(v);
vis[v] = true;
}
}
}
que.pop();
vis[u] = false;
}
}
Floyd算法
逐点试探,看加入这个点是否会影响结果。
for(k=1;k<=n;k++)
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(e[i][j]>e[i][k]+e[k][j]) e[i][j]=e[i][k]+e[k][j];
代码的基本思想就是:最开始只允许经过1号顶点进行中转,接下来只允许经过1和2号顶点进行中转……允许经过1~n号所有顶点进行中转,来进行松弛操作。
Floyd算法允许负权边不允许负权图。(否则可以一直走负圈)
LL power(LL bas, LL exp)
{
LL ans = 1, sqr = bas%MOD;
while(exp > 0)
{
if(exp & 1) ans = (ans*sqr) % MOD;
sqr = (sqr*sqr) % MOD;
exp >>= 1;
}
return ans;
}
查询和修改复杂度都为log(n)
原理:https://blog.csdn.net/c20190102/article/details/70841745
int lowbit(int t)
{
return t&(-t);
}
void update(int x,int val)
{
for(int i=x;i<=n;i+=lowbit(i))
tree[i]+=val;
}
int getsum(int x)
{
int ans=0;
for(int i=x;i>0;i-=lowbit(i))
ans+=tree[i];
return ans;
}
//在main中的初始化
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
update(i,a[i]);
}