algorithm note
算法笔记
C++的函数与库
函数
iomanip
1.介绍:
在c++程序里面经常见到下面的头文件
#include <iomanip>
io代表输入输出,manip是manipulator(操纵器)的缩写(在c++上只能通过输入缩写才有效。)
2.作用:
主要是对cin,cout之类的一些操纵运算子,比如setfill,setw,setbase,setprecision等等。它是I/O流控制头文件,就像C里面的格式化输出一样.以下是一些常见的控制函数的:
dec 置基数为10 相当于"%d"
hex 置基数为16 相当于"%X"
oct 置基数为8 相当于"%o"
setfill( 'c' ) 设填充字符为c
setprecision( n ) 设显示有效数字为n位
setw( n ) 设域宽为n个字符
这个控制符的意思是保证输出宽度为n。如:
cout << setw( 3 ) << 1 << setw( 3 ) << 10 << setw( 3 ) << 100 << endl; 输出结果为
_ _1_10100 (默认是右对齐)当输出长度大于3时(<<1000),setw(3)不起作用。
▲ setw(n)用法: 通俗地讲就是预设宽度
如 cout<<setw(5)<<255<<endl;
结果是:
(空格)(空格)255
▲setfill(char c) 用法 : 就是在预设宽度中如果已存在没用完的宽度大小,则用设置的字符c填充
如 cout<<setfill(‘@‘)<<setw(5)<<255<<endl;
结果是:
@@255
应当指出,setfill和setw只作用于紧随其后的部分,例如
cout<<setfill('')<<setw(6)<<123<<456;的运行结果为123456,这里setfill('')<<setw(6)只对 123 起作用输出了**123,456作为另一部分随后输出。
▲setbase(int n) : 将数字转换为 n 进制.
如 cout<<setbase(8)<<setw(5)<<255<<endl;
cout<<setbase(10)<<setw(5)<<255<<endl;
cout<<setbase(16)<<setw(5)<<255<<endl;
结果是:
(空格)(空格)377
(空格)(空格) 255
(空格)(空格)(空格) f f
▲ setprecision用法
使用setprecision(n)可控制输出流显示浮点数的数字个数。 C++默认的流输出数值有效位是6。
如果setprecision(n)与setiosflags(ios::fixed)合用,可以控制小数点右边的数字个数。setiosflags(ios::fixed)是用定点方式表示实数。
如果与setiosflags(ios::scientific)合用, 可以控制指数表示法的小数位数。setiosflags(ios::scientific)是用指数方式表示实数。
setiosflags(ios::fixed) 固定的浮点显示
setiosflags(ios::scientific) 指数表示
setiosflags(ios::left) 左对齐
setiosflags(ios::right) 右对齐
setiosflags(ios::skipws) 忽略前导空白
setiosflags(ios::uppercase) 16进制数大写输出
setiosflags(ios::lowercase) 16进制小写输出
setiosflags(ios::showpoint) 强制显示小数点
setiosflags(ios::showpos) 强制显示符号
举例:
#include<iostream>
#include<iomanip>
usingnamespacestd;
int main()
{
cout<<12345.0<<endl;//输出"123456"
cout<<setiosflags(ios::fixed)<<setprecision(3)<<1.2345<<endl;//输出"1.234"(遵循四舍六入五成双的原则,而不是四舍五入的原则)
cout<<setiosflags(ios::scientific)<<12345.0<<endl;//输出"1.23e+004"
cout<<setprecision(3)<<12345.0<<endl;//输出"1.23e+004"
return 0;
}
四舍六入五成双的原则:
对于 [位数](http://baike.baidu.com/view/346281.htm) 很多的 [近似数](http://baike.baidu.com/view/120362.htm) ,当 [有效位数](http://baike.baidu.com/view/721666.htm) 确定后,其后面多余的数字应该舍去,只保留 [有效数字](http://baike.baidu.com/view/21016.htm) 最末一位,这种修约(舍入)规则是“四舍六入五成双”,也即“4舍6入5凑偶”这里“四”是指≤4 时舍去,"六"是指≥6时进上,"五"指的是根据5后面的数字来定,当5后有数时,舍5入1;当5后无有效数字时,需要分两种情况来讲:①5前为 [奇数](http://baike.baidu.com/view/20853.htm) ,舍5入1;②5前为偶数,舍5不进。(0是偶数)
c++中的const
const 是 constant 的缩写,本意是不变的,不易改变的意思。在 C++ 中是用来修饰内置类型变量,自定义对象,成员函数,返回值,函数参数。
C++ const 允许指定一个语义约束,编译器会强制实施这个约束,允许程序员告诉编译器某值是保持不变的。如果在编程中确实有某个值保持不变,就应该明确使用const,这样可以获得编译器的帮助。
一、const修饰普通类型的变量
const int a = 7; int b = a; // 正确 a = 8; // 错误,不能改变
a 被定义为一个常量,并且可以将 a 赋值给 b,但是不能给 a 再次赋值。对一个常量赋值是违法的事情,因为 a 被编译器认为是一个常量,其值不允许修改。
接着看如下的操作:
实例
#include<iostream>
using namespace std;
int main(void)
{
const int a = 7;
int *p = (int*)&a;
*p = 8;
cout<<a;
system("pause");
return 0;
}
对于 const 变量 a,我们取变量的地址并转换赋值给 指向 int 的指针,然后利用 *p = 8; 重新对变量 a 地址内的值赋值,然后输出查看 a 的值。
从下面的调试窗口看到 a 的值被改变为 8,但是输出的结果仍然是 7。
从结果中我们可以看到,编译器然后认为 a 的值为一开始定义的 7,所以对 const a 的操作就会产生上面的情况。所以千万不要轻易对 const 变量设法赋值,这会产生意想不到的行为。
如果不想让编译器察觉到上面到对 const 的操作,我们可以在 const 前面加上**Volatile **关键字。
**Volatile **关键字跟 const 对应相反,是易变的,容易改变的意思。所以不会被编译器优化,编译器也就不会改变对 a 变量的操作。
实例
#include<iostream>
using namespace std;
int main(void)
{
volatile const int a = 7;
int *p = (int*)&a;
*p = 8;
cout<<a;
system("pause");
return 0;
}
输出结果如我们期望的是 8。
二、const 修饰指针变量。
const 修饰指针变量有以下三种情况。
- A: const 修饰指针指向的内容,则内容为不可变量。
- B: const 修饰指针,则指针为不可变量。
- C: const 修饰指针和指针指向的内容,则指针和指针指向的内容都为不可变量。
对于 A:
const int *p = 8;
则指针指向的内容 8 不可改变。简称左定值,因为 const 位于 * 号的左边。
对于 B:
int a = 8;
int* const p = &a;
*p = 9; // 正确
int b = 7;
p = &b; // 错误
对于 const 指针 p 其指向的内存地址不能够被改变,但其内容可以改变。简称,右定向。因为 const 位于 * 号的右边。
对于 C: 则是 A 和 B的合并
int a = 8;
const int * const p = &a;
这时,const p 的指向的内容和指向的内存地址都已固定,不可改变。
对于 A,B,C 三种情况,根据 const 位于 * 号的位置不同,我总结三句话便于记忆的话:"左定值,右定向,const修饰不变量"。
三、const参数传递和函数返回值。
对于 const 修饰函数参数可以分为三种情况。
A:值传递的 const 修饰传递,一般这种情况不需要 const 修饰,因为函数会自动产生临时变量复制实参值。
实例
#include<iostream>
using namespace std;
void Cpf(const int a)
{
cout<<a;
// ++a; 是错误的,a 不能被改变
}
int main(void)
{
Cpf(8);
system("pause");
return 0;
}
B:当 const 参数为指针时,可以防止指针被意外篡改。
实例
#include<iostream>
using namespace std;
void Cpf(int *const a)
{
cout<<*a<<" ";
*a = 9;
}
int main(void)
{
int a = 8;
Cpf(&a);
cout<<a; // a 为 9
system("pause");
return 0;
}
C:自定义类型的参数传递,需要临时对象复制参数,对于临时对象的构造,需要调用构造函数,比较浪费时间,因此我们采取 const 外加引用传递的方法。
并且对于一般的 int、double 等内置类型,我们不采用引用的传递方式。
实例
#include<iostream>
using namespace std;
class Test
{
public:
Test(){}
Test(int _m):_cm(_m){}
int get_cm()const
{
return _cm;
}
private:
int _cm;
};
void Cmf(const Test& _tt)
{
cout<<_tt.get_cm();
}
int main(void)
{
Test t(8);
Cmf(t);
system("pause");
return 0;
}
结果输出 8。
对于 const 修饰函数的返回值。
Const 修饰返回值分三种情况。
A:const 修饰内置类型的返回值,修饰与不修饰返回值作用一样。
实例
#include<iostream>
using namespace std;
const int Cmf()
{
return 1;
}
int Cpf()
{
return 0;
}
int main(void)
{
int _m = Cmf();
int _n = Cpf();
cout<<_m<<" "<<_n;
system("pause");
return 0;
}
B: const 修饰自定义类型的作为返回值,此时返回的值不能作为左值使用,既不能被赋值,也不能被修改。
C: const 修饰返回的指针或者引用,是否返回一个指向 const 的指针,取决于我们想让用户干什么。
四、const修饰类成员函数
const 修饰类成员函数,其目的是防止成员函数修改被调用对象的值,如果我们不想修改一个调用对象的值,所有的成员函数都应当声明为 const 成员函数。
注意:const 关键字不能与 static 关键字同时使用,因为 static 关键字修饰静态成员函数,静态成员函数不含有 this 指针,即不能实例化,const 成员函数必须具体到某一实例。
下面的 get_cm()const; 函数用到了 const 成员函数:
实例
#include<iostream>
using namespace std;
class Test
{
public:
Test(){}
Test(int _m):_cm(_m){}
int get_cm()const
{
return _cm;
}
private:
int _cm;
};
void Cmf(const Test& _tt)
{
cout<<_tt.get_cm();
}
int main(void)
{
Test t(8);
Cmf(t);
system("pause");
return 0;
}
如果 get_cm() 去掉 const 修饰,则 Cmf 传递的 const _tt 即使没有改变对象的值,编译器也认为函数会改变对象的值,所以我们尽量按照要求将所有的不需要改变对象内容的函数都作为 const 成员函数。
如果有个成员函数想修改对象中的某一个成员怎么办?这时我们可以使用 mutable 关键字修饰这个成员,mutable 的意思也是易变的,容易改变的意思,被 mutable 关键字修饰的成员可以处于不断变化中,如下面的例子。
实例
#include<iostream>
using namespace std;
class Test
{
public:
Test(int _m,int _t):_cm(_m),_ct(_t){}
void Kf()const
{
++_cm; // 错误
++_ct; // 正确
}
private:
int _cm;
mutable int _ct;
};
int main(void)
{
Test t(8,7);
return 0;
}
这里我们在 Kf()const 中通过 ++_ct; 修改 _ct 的值,但是通过 ++_cm 修改 _cm 则会报错。因为 ++_cm 没有用 mutable 修饰。
c++ 的绝对值函数abs( )
std::abs, std::labs, std::llabs, std::imaxabs
Defined in header <cstdlib>
Defined in header <cmath>
int abs( int n );
long abs( long n );
long long abs( long long n );
(since C++11)
Defined in header <cstdlib>
long labs( long n );
long long llabs( long long n );
(since C++11)
Defined in header <cinttypes>
std::intmax_t abs( std::intmax_t n );
(since C++11)
std::intmax_t imaxabs( std::intmax_t n );
(since C++11)
对于不同数据类型取绝对值
关于while(cin>>value)
#include <iostream>
using namespace std;
int main()
{
int sum = 0, value = 0;
while (cin >> value)
sum += value;
cout << "Sum is: " << sum << endl;
return 0;
}
在C++11新标准下,IO标准库定义了一个向bool的显式类型转换运算符。
while (cin >> value)
while语句的条件执行输入运算符>>,它负责将数据从cin读到value并返回cin(输出运算符<<和输入运算符>>的调用结果都是返回其左侧运算对象)。为了对条件求值,cin被istream operator bool类型转换函数隐式地执行了转换,成为bool类型。如果cin的条件状态是good,则该函数返回为真,否则返回为假。
在while循环中,cin只有在遇到文件结束符(end-of-file),或遇到一个无效输入时(例如输入的值不是一个整数),cin的状态会变为无效退出循环。
从键盘输入文件结束符(end-of-file):
在Windows系统中,输入文件结束符的方法是按Ctrl+Z,然后按Enter或Return键。
在Unix系统中,包括在Mac OS X系统中,文件结束符输入Ctrl+D。
代码运行结果:
依次从键盘输入1,30,5,然后按Ctrl+Z,获得结果如下:
依次从键盘输入2,1,3,e(无效输入),获得结果如下:
fill函数,fill与memset函数的区别
-
memset函数
- 按照字节填充某字符
- 在头文件
<cstring>
里面
-
fill函数
- 按照单元赋值,将一个区间的元素都赋同一个值
- 在头文件
<algorithm>
里面
-
因为memset函数按照字节填充,所以一般memset只能用来填充char型数组,(因为只有char型占一个字节)如果填充int型数组,除了0和-1,其他的不能。因为只有00000000 = 0,-1同理,如果我们把每一位都填充“1”,会导致变成填充入“11111111”
-
而fill函数可以赋值任何,而且使用方法特别简便:
-
fill(arr, arr + n, 要填入的内容);
-
例如:
#include <cstdio> #include <algorithm> using namespace std; int main() { int arr[10]; fill(arr, arr + 10, 2); return 0; }
- vector也可以:
#include <algorithm> #include <vector> #include <iostream> using namespace std; int main(){ vector<int> v{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; fill(v.begin(), v.end(), -1); return 0; }
-
而memset的使用方法是:
#include <iostream> #include <cstring> using namespace std; int main(){ int a[20]; memset(a, 0, sizeof a); return 0; }
-
容器
vector
一、关于vector
vector容器可以认为是一个变长的数组,可以存放任何类型的元素,也可以嵌套使用构成多维数组,使用该类型的基本操作可以简化数据的存储和使用.
//vector数组的定义
//1.常规类型的定义
vector<typename> v; //直接用类型名字定义
//例如:
vector<int> v; //整型
vector<double> v; //浮点型
vector<Node> v; //结构体类型
//2.指定大小的定义
vector<int> v(n); //定义存放n个int型的vector容器
vector<int> v(n,15); //基本类型可以定义时,直接初始化为n个统一的值
//3.二维数组的定义的三种方法
vector<vector<int> > v; //v是包含了多个vect容器的vector容器,> >之间的空格不能省
vector<int> v[100]; //定义100个存放int型数据的变长数组,其中v[0]~v[99]都各是一个变长数组
//二维数组的第二种写法的以为长度已经固定为100,这是和第一种写法的区别
vector<vector<int> > v(m,vector<int> (n));
//定义一个m*n的二维数组
//vector数组的遍历
//vector数组的遍历可以采用常规数组的遍历方法,直接使用下标,或者使用迭代器(类似于指针)的方法
v[i]=0;
v.begin()+i;
//vector数组的常用函数操作,以int型数组为例
vector<int> v;
v.push_back(num); //将num插入到v的尾部,对结构体数组而言,该函数可以插入结构体类型的变量
v.pop_back(); //删除vector尾部的元素
v.size(); //返回数组v的大小
v.clear(); //将v清空
v.back(); //取容器尾部的元素
v.resize(n); //把容器大小需修改为n
v.reserve(n); //把容器的存储能力修改到n,但是size不变
vector<int>:: iterator it=v.begin()+3; //定义一个数组v的迭代器指向第三个位置
v.insert(it,13); //在迭代器it指向的位置插入元素13
v.erase(it); //删除迭代器指向的元素
v.erase(first,last); //删除迭代器[first,last) 左闭右开的区间内的元素
v1.assign(v2.begin(),v2.end()); //将v2数组赋值给v1,会清除v1以前的内容
v.insert(v.end(),v1.begin(),v1.end()); //将v1插入到v的后边
C++中的vector<>v 容器在处理字符串问题时非常好用,对于整个容器的判空使用v.empty();函数就可以办的到,但是对于具体的vector中的某一个元素是否为空。
下面以vector
元素的引用和数组一样对其下标进行操作就可以了
二、vector结构体
vector容器中存放结构体变量有两种方式:
1)存放结构体变量本身
2)存放结构体变量的指针
先介绍第一种方式,存放结构体变量到vector容器中,示例如下:
结构体
struct stu
{
int num;
float grade;
};
定义一个结构体变量的vector容器 std::vector<stu> v;
添加
stu student;
int num;
float grade;
std::cin>>num>>grade;
student.num = num;
student.grade = grade;
v.push_back(student);
遍历
for(std::vector<stu>::iterator it=v.begin();it!=v.end();it++)
{
std::cout<<(*it).num<<" "<<(*it).grade<<std::endl;
}
sort自定义比较函数
bool compare(const stu& st1,const stu& st2)
{
return st1.grade>st2.grade;
}
sort(v.begin(),v.end(),compare);
完整代码:
#include <iostream>
#include <vector>
#include <algorithm>
struct stu
{
int num;
float grade;
};
bool compare(const stu& st1,const stu& st2)
{
return st1.grade>st2.grade;
}
int main()
{
int n,k;
std::cin>>n>>k;
std::vector<stu> v;
for(int i=0;i<n;i++)
{
stu student;
int num;
float grade;
std::cin>>num>>grade;
student.num = num;
student.grade = grade;
v.push_back(student);
}
sort(v.begin(),v.end(),compare);
for(std::vector<stu>::iterator it=v.begin();it!=v.end();it++)
{
std::cout<<(*it).num<<" "<<(*it).grade<<std::endl;
}
return 0;
}
有另外一种方式是将结构体变量的指针放在vector中,结构体变量放在内存堆中,
参考:http://blog.csdn.net/feliciafay/article/details/9128385
定义一个结构体变量指针的vector容器
std::vector<stu*> v;
添加
stu *student = new stu();
int num;
float grade;
std::cin>>num>>grade;
student->num = num;
student->grade = grade;
v.push_back(student);
遍历
for(std::vector<stu*>::iterator it=v.begin();it!=v.end();it++)
{
std::cout<<(*it)->num<<" "<<(*it)->grade<<std::endl;
}
sort自定义排序
bool compare(const stu* st1,const stu* st2)
{
return st1->grade>st2->grade;
}
sort(v.begin(),v.end(),compare);
完整代码:
#include <iostream>
#include <vector>
#include <algorithm>
struct stu
{
int num;
float grade;
};
bool compare(const stu* st1,const stu* st2)
{
return st1->grade>st2->grade;
}
int main()
{
int n,k;
std::cin>>n>>k;
std::vector<stu*> v;
for(int i=0;i<n;i++)
{
stu *student = new stu();
int num;
float grade;
std::cin>>num>>grade;
student->num = num;
student->grade = grade;
v.push_back(student);
}
sort(v.begin(),v.end(),compare);
for(std::vector<stu*>::iterator it=v.begin();it!=v.end();it++)
{
std::cout<<(*it)->num<<" "<<(*it)->grade<<std::endl;
}
return 0;
}
三、关于vector中的迭代器
迭代器简介
迭代器是一种遍历容器内元素的一种数据类型,这种数据类型感觉有点像指针,我们理解的时候可以理解为欸带起用来指向容器中某个元素。
string,vector,[],很少用[],更常用的访问方式是迭代器(更通用)。
通过迭代器,我们就可以读容器中的元素值,读string中的每个字符,还可以修改某个迭代器所指向的元素值。
容器的迭代器类型
vector<int> v{1, 2 ,3};
vector<int>::iterator iter;
//定义了一个迭代器,也必须是vector<int>
//把整个的 vector
//当我们用这个类型定义变量的时候,这个变量就是一个迭代器,这里iter就是个迭代器。
迭代器的begin()/end()操作,反向迭代器rbgin()/rend操作。
//begin()/end() 用来返回迭代类型。 rbegin()/rend()用来返回迭代类型。
1、begin()返回一个迭代器类型
iter = iv.begin(); //如果容器中有元素,则begin返回迭代器,指向容器的第一个元素相当于iv[0]
2、end():返回的是一个迭代器类型,返回的迭代器指向的是并不是末端元素,而是末端元素的后边,我们就理解成指向的是一个不存在的元素。
iter = iv.end();
3、如果一个容器为空的话那么begin和end返回的迭代器就相同。
vector<int> i {1, 2, 3, 4};
vector<int>::iterator iter_begin;
vector<int>::iterator iter_end;
iter_begin = i.begin();
iter_end = i.end();
4、通用的访问方法
vector<int> i {1, 2, 3, 4};
for (vector<int>::iterator iter = i.begin; iter != i.end(); iter++){
cout << *iter << endl;
}
//反向迭代器
for (vector<int>::reverse_iterator riter = i.rbegin; iter != i.rend(); iter++){
cout << *riter << endl;
}
反向迭代器原理
迭代器运算符
*iter
:返回迭代器iter所指向的元素引用。必须要保证中灌迭代器指向的是有效的元素,不能指向end,因为end是末端元素后边,也就是一个不存在的元素。
iter++
:指向当前迭代器的下一个元素。已经指向end后就不能在++
iter--
:指向容器中的上一个元素,指向begin时不能再--
iter1 == iter2: iter1 != iter2
,判断两个迭代器是否相等,否则就不等。
struct student { int num;};
vector<student> sv;
student stu;
stu.num = 10;
sv.push_back(stu);
vector<student>::iterator iter;
iter = sv.begin();
cout << (*iter).num << endl;
cout << iter->num << endl;
const_iterator迭代器:const:常量,const_iterator迭代器,表示值不能改变的意思,这里的值不能改变表示这个迭代器指向的元素值不能改变,而不是这个迭代器本身不能改变。所以这个迭代器只能读取元素,不能通过这个迭代器改写容器中的元素。
const vector<int> i {1, 2, 3, 4};
vector<int>::const_interator iter; //常量容器必须用常量迭代器读取
for (iter = i.begin; iter != i.end(); iter++){ //可以
cout << *iter << endl;
}
//-----------------------------------------------------------------------
vector<int>::interator iter; //常量容器必须用常量迭代器读取
for (iter = i.begin; iter != i.end(); iter++){ //报错
cout << *iter << endl;
}
//-----------------------------------------------------------------------
vector<int> i {1, 2, 3, 4};
vector<int>::const_interator iter; //常量容器必须用常量迭代器读取
for (iter = i.begin; iter != i.end(); iter++){ //可以
*iter = 4; // 报错,常量迭代器只能读取容器数据,不能修改
cout << *iter << endl;
}
cbegin()和cend(),和begin,end类似。cbegin与cend返回的都是常量迭代器。
for (auto iter = iv.cbegin(), iter != iv.cend(), iter++){
*iter = 4;//报错
}
迭代器失效问题
vector<int> vecvalue{1, 2, 3, 4};
for(auto v: vecvalue){
}
再操作迭代器过程中千万不要改变,也就是不要增加或者删除vector中的元素。往容器中增加或者删除元素,可能导致迭代器失效也就是不能再代表容器中的任何元素,一旦失效,就等与犯了严重的错误,很多情况下程序会直接崩溃报错。
再操作迭代器过程中千万不要改变,也就是不要增加或者删除vector中的元素。往容器中增加或者删除元素,可能导致迭代器失效也就是不能再代表容器中的任何元素,一旦失效,就等与犯了严重的错误,很多情况下程序会直接崩溃报错。
防止迭代器失效最有效的方法就是修改后break出来,重新生成迭代
如何防止迭代器失效
防止迭代器失效要具体查找每种结构的构造方法。
vector<int> vec{1, 2, 3, 4};
vector<int>::iterator beg = vec.begin();
auto end = vec.end();
int icont = 0;
while( beg != vec.end() ){ //每次更新end,防止迭代器失效
vec.insert(beg, 80+icont);
icont++;
if(icont > 5){
break;
}
beg++;
}
释放vector
vector<int> iv = {1, 2, 3};
for(vector<int>::iterator iter = iv.begin(); iter != iv.end; ++iter){
iv.erase(iter); //erase函数,移除iter位置上的元素,返回下一个位置元素;
}
上面的写法报错;
vector<int> iv = {1, 2, 3};
auto beg = iv.begin();
while (iter ! = iv.end()){
iter = iv.erase(iter);
}
while (!iv.empty()){
vector<int>::iterator iter = iv.begin();
iv.erase(iter);
}
样例:
class conf {
public:
char item_name[40];
char item_content[100];
};
char *get_content(vector<conf *> conf_list, const char *pitem) {
vector<conf *>::iterator conf_item;
for (conf_item = conf_list.begin(); conf_item != conf_list.end(); conf_item++) {
if (_stricmp((*conf_item)->item_name, pitem) == 0) {
return (*conf_item)->item_content;
}
cout << (*conf_item)->item_name << endl;
}
return nullptr;
}
int main()
{
conf *conf1 = new conf;
conf *conf2 = new conf;
strcpy_s(conf1->item_name, sizeof(conf1->item_name), "ServerName");
strcpy_s(conf1->item_content, sizeof(conf1->item_content), "1区");
strcpy_s(conf2->item_name, sizeof(conf2->item_name), "ServerID");
strcpy_s(conf2->item_content, sizeof(conf2->item_content), "1001");
vector<conf *> vec{conf1,conf2};
char *p = get_content(vec, "ServerID");
if (p != nullptr) {
cout << p << endl;
}
else
{
cout << "not match" << endl;
}
vector<conf *>::iterator pos;
for (pos = vec.begin(); pos != vec.end(); pos++) {
delete (*pos);
}
vec.clear();
}
版权声明:本文为CSDN博主「lucky0han」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_38278993/article/details/104417489
四、vector的补充
vector使用前准备:
include
using namespace std;
vector采用模板类实现,vector对象的默认构造形式:vector
如:
vector
vector
vector尾部的添加移除操作:
vector.push_back(elem);在容器尾部添加
vector.pop_back();删除最后一个元素
vector数据的存储:
vector有两种形式获取某个索引的值,分别是at和[],比如一个vector vec,取其第5个元素,可以用vec.at(4)或vec[4].
vec.at(idx);返回索引idx所指的数据,如果idx越界,抛出out_of_range异常
vec[idx];返回索引idx所指的数据,越界直接报错
修改:把vec的第5个元素修改为15,两种方式:
vec.at(4)=15;
vec[4]=15;
返回vector的第一个元素和最后一个函数:
vector.front();返回第一个数据
vector.back();返回最后一个数据
修改第一个和最后一个值,例如对于vec:
vec.front()=111;//将vec第一个元素改为111
vec.back()=199;//将vec最后一个元素修改为199
迭代器:
迭代器是一个“可遍历STL容器内全部或部分元素”的对象,迭代器指出容器中的一个特定位置。迭代器就如同一个指针,提供对一个容器中的对象的访问方法,并且可以定义容器中对象的范围。
迭代器种类:
1.输入迭代器,也称“只读迭代器”,它从容器中读取元素,一次读入一个元素向前移动,只支持一遍算法,同一个输入迭代器不能两遍遍历一个序列。
2.输出迭代器,也称“只写迭代器”,一次写入一个元素向前移动,也只支持一遍算法。
3.正向迭代器,组合输入迭代器和输出迭代器的功能,还可以多次解析一个迭代器指定的位置,可以对一个值进行多次读/写。
4.双向迭代器:组合正向迭代器的功能,还可以通过--操作符向后移动位置。
5.随机访问迭代器:组合双向迭代器的功能,可以向前向后跳任意位置,可以直接访问容器中任何位置的元素。
it为迭代器对象
双向迭代器支持的操作:
it++,++it,it--,--it,*it,itA=itB,itA==itB,itA!=itB
其中list,set,multiset,map,multimap支持双向迭代器
随机访问迭代器支持的操作:
在双向迭代器的操作基础上添加:
it+=i,it-=i,it+i(或it=it+i),it[i],itA<itB,itA<=itB,itA>itB,itA>=itB
其中,vector,deque支持随机访问迭代器
vector与迭代器配合使用:
vec.begin()//返回容器第一个元素的迭代器
vec.end();//返回容器最后一个元素之后的迭代器
例如vecInt是vector
vector
it=vecInt.begin();//此时*it==1
运行++it;//或者it++;,此时*it==3,前++的效率比后++的效率高,前++返回引用,后++返回值
反向迭代器:
vector
vec.rbegin();//返回vec中倒数第一个元素的迭代器
vec.rend();//返回容器中倒数最后一个元素之后的迭代器
vector与反向迭代器配合使用:看代码
迭代器其他两种声明方式:
vector
vector
这两种分别是正向迭代器和反向迭代器的只读形式。使用这两种迭代器时,将不会修改容器中的值。
备注:容器中的insert和erase方法只接受这四种容器中的iterator,其他三种不支持。《Effective STL》建议尽量使用iterator取代其他三种。
vector对象的带参数构造
vector(beg,end);//构造函数将[beg,end)区间中的元素拷贝给本身。区间为左闭右开,即从beg开始到end结束,但不包括end。
vector(n,elem);//构造函数将n个elem拷贝给本身。
vector(const vector &vec);拷贝构造函数。
//见代码举例
vector的赋值
vector.assign(beg,end);//将[beg,end)区间中的数据拷贝赋值给本身。注意该区间和上面一样,是左闭右开的区间。
vector.assign(n,elem);//将n个elem拷贝赋值给本身。
vector & operator=(const vector &vec);//重载等号操作符
vector.swap(vec);//将vec与本身的元素互换
vector的大小
vector.size();//返回容器中元素的个数
vector.empty();//判断容器是否为空
vector.resize(num);//重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则尾部超出容器长度的元素被删除。
vector.resize(num,elem);//重新指定容器长度为num,变长则以elem值填充新位置;变短则超出元素被删除。
vector的插入
vector.insert(pos,elem);//在pos位置插入一个elem元素的拷贝,返回新数据的位置
vector.insert(pos,n,elem);//在pos位置插入n个elem数据,无返回值
vector.insert(pos,beg,end);//在pos位置插入[beg,end)区间的数据,无返回值。
vector的删除:
vector.clear();移除容器的所有数据
vector.erase(beg,end);//删除[beg,end)区间的数据,返回下一个数据的位置。
vector.erase(pos);//删除pos位置的数据,返回下一个数据的位置
例子:删除所有3
int iArray[]={1,3,2,3,3,3,4,3,5,3};
vector
for(vector
{
if(*it==3)
{
it=vecInt.erase(it);//以迭代器为参数,删除元素3,并把数据删除后的下一个元素位置作为返回值
//此时,不执行++it;
}
else
{
++it;
}
}
版权声明:本文为CSDN博主「张冰洋的天空」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zby1001/article/details/52887562
递归入门
面试题16.11.跳水板
难度: 简单
🔗链接: $$
Ⅱ、k==0时,一种情况,总长度为空,如果计为0的话会 WA
Ⅲ、一般情况,总长度有多种情况,用vector存储
2、考虑递归函数表达式
主要用于一般情况,特殊情况不在考虑范围内,如果f(x,y)表示x块短板,y块长板的板子长度的组合情况,x+y=k,那么即有:$$ f(x, y)=f(x-1,y+1).........................f(0,y)"y=k" $$
3、考虑递归每级需要做什么
由于上面的分级,因此每级递归只需要考虑一种增加的情况,只需将板子长度计算出来push到vector中即可。
AC代码:
class Solution {
public:
vector<int>vec;
vector<int> divingBoard(int shorter, int longer, int k) {
if(shorter==longer && k!=0){
vec.push_back(shorter*k);
return vec;
}
else{
if(k == 0) {
return vector<int>{};
}
help_fx(shorter,longer,k,0);
sort(vec.begin(),vec.end());
vec.erase(unique(vec.begin(),vec.end()),vec.end());
return vec;
}
}
void help_fx(int shorter,int longer,int x,int y){
if(x<0||y<0) return ;
int length=shorter*x+longer*y;
vec.push_back(length);
help_fx(shorter,longer,x-1,y+1);
}
};
面试题07.重建二叉树
🔗链接:https://leetcode-cn.com/problems/zhong-jian-er-cha-shu-lcof/
解题思路:分治算法
同柳神博客里面的两个例子:
🔗已知后序与中序输出前序(先序)链接:https://www.liuchuo.net/archives/2090
🔗已知前序(先序)与中序输出后序链接:https://www.liuchuo.net/archives/2087
AC代码:
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode(int x) : val(x), left(NULL), right(NULL) {}
* };
*/
class Solution {
public:
TreeNode* buildTree(vector<int>& preorder, vector<int>& inorder) {
return helpfx(preorder.begin(),preorder.end(),inorder.begin(),inorder.end());
}
TreeNode* helpfx(vector<int>::iterator pre_start,vector<int>::iterator pre_end,
vector<int>::iterator in_start,vector<int>::iterator in_end
){
if(in_end==in_start) return NULL;
TreeNode *t=new TreeNode(*pre_start);
auto root=find(in_start,in_end,*pre_start);
t->left=helpfx(pre_start+1,pre_start+1+(root-in_start),in_start,root);
t->right=helpfx(pre_start+1+(root-in_start),pre_end,root+1,in_end);
return t;
}
};