C4团体天梯赛STL库使用手册

scanf:

1. 一个一个地输入%c时,考虑好是否要在最后加getchar()。

2. While(scanf(“%d %d”,&a,&b)!=EOF){}

While(scanf(“%d”,&a)&&a){}

While(gets(s)!=NULL){}

While(scanf(“%s”,a)!=EOF){}

输入%d形式时可以时001这种,会自动转为数字1

while (scanf("%s%s", word1,word2) != EOF),输入的两个字符串中间可以有任意个空格

加速cin\cout

sync_with_stdio函数是一个“是否兼容stdio”的开关,C++为了兼容C,保证程序在使用了std::printf和std::cout的时候不发生混乱,将输出流绑到了一起。可以在IO之前将stdio解除绑定,这样做了之后要注意不要同时混用cout和printf 之类。

在默认的情况下cin绑定的是cout,每次执行 << 操作符的时候都要调用flush,这样会增加IO负担。可以通过tie(0)(0表示NULL)来解除cin与cout的绑定,进一步加快执行效率。

#include <iostream>

Using namespace std;

int main()

{

    std::cin.tie(0);

std::ios::sync_with_stdio(false);

// 打消iostream的输入输出缓存,可以节省许多时间

//使效率与scanf与printf相差无几  

}

cin/cin.getline/getline

1.当cin>>从缓冲区中读取数据时,若缓冲区中第一个字符是空格、tab或换行这些分隔符时,cin>>会将其忽略并清除,继续读取下一个字符,若缓冲区为空,则继续等待。但是如果读取成功,字符后面的分隔符是残留在缓冲区的,cin>>不做处理。

2.cin.get()从输入缓冲区读取单个字符时不忽略分隔符,直接将其读取;

 ①char a;char b;

    a=cin.get();

    cin.get(b);// cin.get(char)如果成功返回的是cin对象,因此可以支持链式操作

        //如cin.get(b).get(c)

② char a;

    char array[20]={NULL};

    cin.get(array,20);

    cin.get(a);

    cout<<array<<" "<<(int)a<<endl;

//输入:123456789[回车],输出:

cin.get(array,20);读取一行时,遇到换行符时结束读取,但是不对换行符进行处理,换行符仍然残留在输入缓冲区。第二次由cin.get()将换行符读入变量b,打印输入换行符的ASCII码值为10。

※这也是cin.get()读取一行与使用getline读取一行的区别所在。getline读取一行字符时,默认遇到’\n’时终止,并且将’\n’直接从输入缓冲区中删除掉,不会影响下面的输入处理。

※2. cin.getline(cin,char*) :getline读取一行字符时,默认遇到’\n’时终止,并且将’\n’直接从输入缓冲区中删除掉,不会影响下面的输入处理。

    cin.getline(array,20); //或者指定结束符,使用下面一行,默认使用换行符作为结束

    //cin.getline(array,20,'\n');

    char a;

    int b;

    float c;

    string str;

    cin>>a>>b>>c>>str;

    cout<<a<<" "<<b<<" "<<c<<" "<<str<<endl;

 

    string test;

    getline(cin,test);//不阻塞

    cout<<"test:"<<test<<endl;

system("pause");

//输入:[回车][回车][回车]a[回车]5[回车]2.33[回车]hello[回车],输出结果是:

cin>>对缓冲区中的第一个换行符视而不见,采取的措施是忽略清除,继续阻塞等待缓冲区有效数据的到来。但是,getline()读取数据时,并非像cin>>那样忽略第一个换行符,getline()发现cin的缓冲区中有一个残留的换行符,不阻塞请求键盘输入,直接读取,送入目标字符串后,再将换行符替换为空字符’\0’,因此程序中的test为空串。

3.getline(在<string>)遇到结束符时,会将结束符一并读入指定的string中,再将结束符替换为空字符。因此,进行从键盘读取一行字符时,建议使用getline,较为安全(getline读不到\n’)。

getline利用cin可以从标准输入设备键盘读取一行,当遇到如下三种情况会结束读操作:1)到文件结束,2)遇到函数的定界符,3)输入达到最大限度。

    string str;

    getline(cin,str);

    cout<<str<<endl;

//输入:hello world[回车],输出:

4. cin.ignore(INT_MAX, '\n');//清空缓存区

    cin >> a;

    cin.ignore(INT_MAX, '\n');

    cin >> b;

    cout << a << ',' << b << endl;

效果如下:

判断两个分数大小

比如a/b>c/d,为了避免小数,可以用a*d>c*b

输出格式问题

1.scanf/printf: 输出long double 必须是Lf;

float输入是f,double输入是lf;二者输出都必须是f!!!特别重要,不然会wa!!!

2.数据前面加:

0x数据     十六进制

0数据      八进制

直接数据   十进制

%d  十进制     %o八进制      %x   十六进制(小写)        %X十六进制(大写)

 

%llu unsigned long long int输入输出

3、输出%

printf("%.2f%%\n",a);//输出%

浮点数注意事项:

1.判断相等

const double eps=1e-4;

fabs(a-b)<=eps;

这样来判断相等。

2.输入输出!

float 输入&输出:%f

double 输入%lf  输出%f

3.转int时的精度问题(一定要+0.5再转int):

要注意浮点数转成int会有误差,比如double型的0.1*int型的60可能会出现=5,应该(int)((0.1*60)+0.5)

Wrong answer

1.应该用long long而不是int。%mod的时候保险一点,乘一个数就要mod一下,不然WA到哭。

或者是用int型的数值去运算了,但是结果的范围是ll,那应该用1LL*(运算式)!

2. 0<=x1,x2<=1000  or  0<=x1<=x2<=1000  看清楚

4、函数的返回值都正确嘛?CodeBlocks里有时候函数没有返回值不会报错,但是交上去会返回WA,比如返回类型为bool的函数,函数结尾没有明确写明返回什么值得时候是不会报错的。

6、POJ还有PTA上,不使用多组输入但是敲了!=EOF也会返回WA

8、读题读对了吗

9、访问map的关键字之前注意判断当前map中有没有那个关键字。

Runtime error(运行时错误)

程序运行到一半崩溃了

  1. 数组开小了(int数组最大到80000000)
  2. 除以零了!!!!(PAT也会提示“浮点错误”)
  3. 指针越界:int * p; p=(int *)malloc(5 * sizeof(int)); *(p+1000000)=10;(注意检查下是不是有的数没有初始化,导致指针越界。不同的编译平台对于没有赋值的数的初始赋值不同,所以最好还是赋初值比较保险。)
  4. 程序运行中有的数溢出了,出现负数,而这个数又作为数组的下标在后面使用了!(比如最短路算法中,初始化所有的dis[]都为inf,有的时候就会溢出)
  5. 使用已经释放的空间:int * p; p=(int *)malloc(5 * sizeof(int));free(p); *p=10;
  6. 数组开得太大,超出了栈的范围,造成栈溢出:int a[100000000];
  7. 调用函数时最好不要进行类型转化,因为过多的调用函数+类型转换容易造成Runtime Error。
  8. 函数结尾漏写了return?(排序函数里)

段错误

  1. memset不要对string[]数组使用
  2. 头文件没包含全,比如cstring
  3. 一些指向数组下标的变量没有初始化也可能导致段错误
  4. 数据溢出,导致段错误甚至是memory limit exceed

Time Limit Exceed

1、死循环、算法不够优化、数组开太小、用了long long(可能是)

2、循环变量一定尽量不要用long long啊!

3、longlong运算真的特别特别特别慢!有的MOD之后的结果在int之内的可以用1LL*运算:

for example1:

声明成int的val=(1LL*node[i-1].val*p+1LL*q*i+r)%MOD;

比原本就声明成LL的val=(node[i-1].val*p%MOD+q*i%MOD+r%MOD)%MOD;快了好几好几倍!!

for example2:

LL ans[];int phi[];

ans[i]=phi[i*2]/2;//int型运算再赋给LL类型

ans[i]+=ans[i-1];

//反例:ans[i]=ans[i-1]+phi[i*2]/2; 这样的话phi[i*2]/2看作是LL类型的运算了

4、memset 在数据量比较大的时候少用啊,尤其是时间复杂度刚好卡过去的时候,能不用memset就不要用  1e6的memset大概需要300+ms!

5、在循环里不要使用strlen(str),用len=strlen(str)然后用len:for(int j=0;j<len;++j)

6、排序时用到的比较函数,不要多写额外的条件,会导致排序时间的常数变大,在数据量很大的情况下,这种时间差异会被放大。

7、如果某个数的运算结果(比如pow、sqrt)频繁被使用,那么可以先预处理出来

 

ends用“ ”代替

输出空格的话乖乖写“ ”,不要用ends,它不代表空格!

sqrt/pow:

double  sqrt  (double  x)

float  sqrt  (float  x)

long double  sqrt  (long  double x)

//参数与返回值类型一一对应

double  pow (double  base, double  exponent)

double  pow (double  base, int  exponent)

double  pow (float  base, float  exponent)

由于pow最后的结果是double型,所以如果参数都是int,返回的结果也保存在int型变量里,那么应该把结果+0.5,使得double转int是四舍五入的,避免最后的结果直接把小数截断

比如:int a=pow(10,2);//a赋值成了99  

  int a=pow(10,2)+0.5;//a赋值成100

如果需要多次用到某个数的计算结果,可以预处理出来,节省时间

memset:

1.char a[5]; memset(a,’1’,5); //正确

  int  b[5]; memset(b,1,sizeof(b)); //错误,b[0]~b[4]都不是1

  //因为memset是以字节为单位赋值的

2.将某个数组清零 => memset(b,0,sizeof(b)); //高效快捷

 将某个数组全部赋值为无穷大 => memset(a,0x3f,sizeof(a));

  //0x3f3f3f3f的每个字节都是0x3f

  全部为-1 => memset(a,-1,sizeof(a));

  //因为-1的存储全为11111111111…

不同进制的输出

c++中:

头文件iostream中提供控制符:

dec:指示cout以十进制输出。 hex:指示cout以十六进制输出。 oct:指示cout以八进制输出。

int n_max = 42;

    cout<<n_max<<endl;//输出42

    cout<<hex;

    cout<<n_max<<endl;//输出2a

    cout<<oct;

    cout<<n_max<<endl;//输出52

二进制没有直接的输出,可以用bitset

const int num = 8;//num表示bitset声明的位数,即有多少位输出。

int main(){

    int n_max = 42;

    cout<<(bitset<num>)n_max<<endl; //00101010

return 0;

}

c中:

char *itoa(int value, char *string, int radix);

详细解释:itoa是英文integer to array(将int整型数转化为一个字符串,并将值保存在数组string中)的缩写.

参数:value: 待转化的整数。

radix: 是基数的意思,即先将value转化为radix进制的数,范围介于2-36,比如10表示10进制,16表示16进制。

* string: 保存转换后得到的字符串。

返回值:char * : 指向生成的字符串, 同*string。

备注:该函数的头文件是"stdlib.h"

排序

冒泡:

for (j = 1; j < 10; ++j) //假设共有10个数,则需要9趟排序

{

for (i = 0; i < 10 - j; ++i) //第j趟排序比较10-j次

{

if (a[i] > a[i + 1]) //从小到大排序

{

t = a[i];

a[i] = a[i + 1];

a[i + 1] = t;

}

}

}

选择:

for (i = 0; i < (n - 1); ++i)

{

min = i;

for (j = i + 1; j < n; ++j)

{

if (a[j] < a[min])min = j;

} //每次都选出最小的一个

t = a[i];

a[i] = a[min];

a[min] = t;//将最小的那个与a[i]交换

}

3.  sort(对给定区间所有元素进行排序,c++)

#include<algorithm>

①.对普通数组进行排序(可以不写cmp函数,此时默认升序):

bool compare(int a,int b)

{

       return  a<b;   //升序排列

       若改为:return  a>b;   //则为降序

}

int a[20];

sort(a,a+20,compare);

//不写cmp,less<数据类型>()从小到大排序,greater<数据类型>()从大到小排序

②.对结构体进行排序:

(1)

struct node {

int x, y;

bool operator < (const node a) const

{

return x < a.x;

}//<代表升序,>代表降序,x代表根据结构体中x的值进行排序 

}island[MAXN];//将每个岛的坐标存在结构体中

sort(island, island + n);//按x的值升序排列

(2)

假设自己定义了一个结构体node:

 

struct node{

    int a;

    int b;

    double c;

};

bool cmp(node x,node y)

{

     if(x.a!=y.a)  return x.a<y.a;// 先按a值升序排列

     if(x.b!=y.b)  return x.b>y.b;// 如果a值相同,再按b值降序排列

     return  return x.c>y.c;// 如果b还相同,就按c降序排列

}

排序时写sort(arr,arr+100,cmp);

⑤.如果vector<vector<int> >vec;那么使用sort函数会默认将各数组按从小到大排序,即根据第一个值不同的元素的大小来排,注意sort(vec.begin(),vec.end()),vector的操作一般都是函数格式的。

注意,对于最后题目要求把二维数组按一定要求进行排序时,要么,你把保存答案的数组在解题的过程中规定好顺序;要么,用vector容器,最后按规则写sort的cmp函数:

例题:PAT:Path of equal path

从给出的多叉树中,找到从根到叶的某一权值总和的路径,并根据节点权值从大到小输出。

排序规则:sequence {A1, A2, ..., An} is said to be greater than sequence {B1, B2, ..., Bm} if there exists 1 <= k < min{n, m} such that Ai = Bi for i=1, ... k, and Ak+1 > Bk+1.

思路:dfs,但是最后结果排序要注意有要求。可以在输入孩子结点时,把它们按自己的权值由大到小排列,这样dfs出的结果是有序的;要不就把结果路径权值存在vector,最后sort,cmp函数如下:

bool cmp(vector<int>a, vector<int> b){  

 int size1 = a.size(), size2 = b.size();  

 for (int i = 0; i < a.size() && i < b.size(); i++){  

   if (a[i] == b[i])continue;  

else return a[i] > b[i];  

  }  

 if (a.size() > b.size())

return true;  

 else return false;  

}  

字符串

C中字符操作

1)字符串操作

strcpy(p, p1) 复制字符串 //第二个字符串将覆盖掉第一个字符串的所有内容

strncpy(p, p1, n) 复制指定长度字符串

//字符串p1中前n个字符将覆盖掉字符串p中前n个字符

strcat(p, p1) 附加字符串 //将字符串p1接到字符串p的后面

strncat(p, p1, n) 附加指定长度字符串//将字符串p1的前n个字符接到字符串p的后面

strlen(p) 取字符串长度

strcmp(p, p1) 比较字符串

strcasecmp忽略大小写比较字符串

strncmp(p, p1, n) 比较指定长度字符串

strchr(p, c) 在字符串中查找指定字符

strrchr(p, c) 在字符串中反向查找

strstr(p, p1) 查找字符串

strrev(s);字符串反转

2)字符检查

isalpha() 检查是否为字母字符

isupper() 检查是否为大写字母字符

islower() 检查是否为小写字母字符

isdigit() 检查是否为数字

isxdigit() 检查是否为十六进制数字表示的有效字符

isspace() 检查是否为空格类型字符

iscntrl() 检查是否为控制字符

ispunct() 检查是否为标点符号

isalnum() 检查是否为字母和数字

3)内存操作

void *memset(void *dest, int c, size_t count);  

将dest前面count个字符置为字符c.  返回dest的值.  

void *memcpy(void *dest, const void *src, size_t count);  

从src复制count字节的字符到dest. 与memmove功能一样, 只是不能处理src和dest出现重叠.  返回dest的值.

C++中string操作

1.string类重载运算符operator>>用于输入,同样重载运算符operator<<用于输出操作

string str1;

cin >> str1;

    //当用cin>>进行字符串的输入的时候,遇到空格的地方就停止字符串的读取输入

cin.get();

    //这个的作用是读取cin>>输入的结束符,不用对getline的输入产生影响!

getline(cin, str1);

//字符串的行输入,不会读入‘\n‘。

//如果前面是scanf(),注意考虑是否在使用前加getchar()接收换行符什么的

//如果是用getline()接收每一行,那么不用在之前加getchar()

//如果第一个字符就是’\n’那么str1会置为空串,既str1=””

getline(cin,line,'#');

//第三个参数作为指定的行分割字符。也就是读到’#’的时候才结束这一行的读入而不是’\n’

2.string类的构造函数

string str2 = "aaaaa";//最简单的字符串初始化

char *s = "bbbbb";

string str3(s);//用c字符串s初始化

char ch = 'c';

string str4(5, ch);//用n个字符ch初始化

3.string类的字符操作

string str5 = "abcde";

ch = str5[3];//operator[]返回当前字符串中第n个字符的位置

string str6 = "abcde";

ch = str6.at(4);//at()返回当前字符串中第n个字符的位置,并且提供范围检查,当越界时会抛出异常。

4.string的特性描述

string str7 = "abcdefgh";

int size;

size = str7.size();//返回当前字符串的大小

size = str7.length();//返回当前字符串的长度

注意!!string类的length()函数和size()函数一定要注意返回的是无符号数!所以不能把返回值跟-1或者超出范围的数比!

bool flag;

flag = str7.empty();//判断当前字符串是否为空

int len = 10;

str7.resize(len, ch);//把字符串当前大小置为len,并用字符ch填充不足的部分

5.string的赋值

string str8;

str8 = str7;//把字符串str7赋给当前字符串

str8.assign(str7);//把字符串str7赋给当前字符串

str8.assign(s);//用c类型字符串s赋值

str8.assign(s, 2);//用c类型字符串s开始的n个字符赋值

str8.assign(len, ch);//用len个字符ch赋值给当前字符串

str8.assign(str7, 0, 3);//把字符串str7中从0开始的3个字符赋给当前字符串

string str9 = "0123456789";

str8.assign(str9.begin(), str9.end());//把迭代器之间的字符赋给字符串

如果想char类型和string混用的话,最好用vector<char>,再用迭代器实现char[]转换为string

6.string的连接

string str10;

str10 += str9;//把字符串str9连接到当前字符串的结尾

str10.append(s);//把c类型字符串s连接到当前字符串的结尾

str10.append(s, 2);//把c类型字符串s的前2个字符连接到当前字符串的结尾

str10.append(str9.begin(), str9.end());//把迭代器之间的一段字符连接到当前字符串的结尾

str10.push_back('k');//把一个字符连接到当前字符串的结尾

7.string的比较

flag = (str9 == str10);//判断两个字符串是否相等

flag = (str9 != str10);//判断两个字符串是否不相等

flag = (str9 > str10);//判断两个字符串是否大于关系

flag = (str9 < str10);//判断两个字符串是否为小于关系

flag = (str9 >= str10);//判断两个字符串是否为大于等于关系

flag = (str9 <= str10);//判断两个字符串否为小于等于关系

//以下的3个函数同样适用于c类型的字符串,在compare函数中>时返回1,<时返回-1,=时返回0

flag = str10.compare(str9);//比较两个字符串的大小,通过ASCII的相减得出!

flag = str10.compare(6, 12, str9);//比较str10字符串从6开始的12个字符组成的字符串与str9的大小

flag = str10.compare(6, 12, str9, 3, 5);//比较str10字符串从6开始的12个字符组成的字符串与str9字符串从3开始的5个字符组成的字符串的大小

8.string的子字串

string str11;

str11 = str10.substr(10, 15);//返回从下标10开始的15个字符组成的字符串

9.string的交换

str11.swap(str10);//交换str11与str10的值

10.string的查找,查找成功时返回所在位置失败时返回string::npos的值,即是-1 

string str12 = "abcdefghijklmnopqrstuvwxyz";

int pos;

pos = str12.find('i', 0);//从位置0开始查找字符i在当前字符串的位置

pos = str12.find("ghijk", 0);//从位置0开始查找字符串“ghijk”在当前字符串的位置

11.string的替换

string str14 = "abcdefghijklmn";

str14.replace(0, 3, "qqqq");//删除从0开始的3个字符,然后在0处插入字符串“qqqq”,不会替换掉第四个字母,qqqqde……

str14.replace(0, 3, "vvvv", 2);//删除从0开始的3个字符,然后在0处插入字符串“vvvv”的前2个字符,vvqde……

str14.replace(0, 3, "opqrstuvw", 2, 4);//删除从0开始的3个字符,然后在0处插入字符串“opqrstuvw”从位置2开始的4个字符

str14.replace(0, 3, 8, 'c');//删除从0开始的3个字符,然后在0处插入8个字符 c

//上面的位置可以换为迭代器的位置,操作是一样的,在这里就不再重复了!

12.string的插入,下面的位置处亦可以用迭代器的指针表示,操作是一样的

string str15 = "abcdefg";

str15.insert(0, "mnop");//在字符串的0位置开始处,插入字符串“mnop” ,mnopabc……

str15.insert(0, 2, 'm');//在字符串的0位置开始处,插入2个字符m

str15.insert(0, "uvwxy", 3);//在字符串的0位置开始处,插入字符串“uvwxy”中的前3个字符

str15.insert(0, "uvwxy", 1, 2);//在字符串的0位置开始处,插入从字符串“uvwxy”的1位置开始的2个字符

13.string的删除

string str16 = "gfedcba";

string::iterator it;

it = str16.begin();

it++;

str16.erase(it);//删除it指向的字符,返回删除后迭代器的位置

str16.erase(it, it + 3);//删除it和it+3之间的所有字符,返回删除后迭代器的位置

str16.erase(2);//删除从字符串位置3以后的所有字符,返回位置3前面的字符

str16.eraser(pos,2);//删除从字符串位置pos开始的2个字符

14.string的翻转

reverse(s.begin(),s.end());//原地翻转

s1.assign(s.rbegin(), s.rend());//s不翻转,s1为翻转的结果

字符串与数字转换

一.C中的sprintf、sscanf函数

1. 可以用sprintf函数将数字转换成字符串

         int H=1, M=2, S=3;

         string time_str;

         char ctime[10];

         sprintf(ctime, "%d:%d:%d", H, M, S); // 将整数转换成字符串

         time_str=ctime; // 结果1:2:3

2. 与sprintf对应的是sscanf函数, 可以将字符串转换成数字

     char str[] = "15.455";

     int i;

     float fp;

     sscanf( str, "%d", &i ); // 将字符串转换成整数 i = 15

     sscanf( str, "%f", &fp ); // 将字符串转换成浮点数 fp = 15.455000

二.C标准库atoi, atof, atol, atoll(C++11标准)

函数将字符串转换成int,double, long, long long 型

1. itoa函数

char *itoa(int value, char *string, int radix);

value: 待转化的整数。

radix: 是基数的意思,即先将value转化为radix进制的数,范围介于2-36,比如10表示10进制,16表示16进制。

* string: 保存转换后得到的字符串。

返回值:

char * : 指向生成的字符串, 同*string。

备注:该函数的头文件是"stdlib.h"

2. atoi

C语言库函数名: atoi

  功 能: 把字符串转换成整型数

  函数说明: atoi()会扫描参数nptr字符串,检测到第一个数字或正负符号时开始做类型转换,之后检测到非数字或结束符 \0 时停止转换,返回整型数。

  原型: int atoi(const char *nptr);

  需要用到的头文件: #include <stdlib.h>

STL

解决容器eraser操作之后迭代器变为野指针的问题

        set<int>s;

set<int>::iterator it,tit;//用一个替代的迭代器做eraser操作,这样就可以边遍历边eraser啦

for(it=s.begin();it!=s.end();++it){

v=*it;

if(mp[make_pair(u,v)]==flag){

Q.push(node(v,cost+1));

tit=it;//注意迭代器经过erase会变成野指针,所以要用一个代替it

s.erase(tit);

}

}

lower_bound()/upper_bound()(数组必须有序!!)

1.lower_bound(a,a+n,val):函数lower_bound()在有序的first和last中的前闭后开区间(已升序排好)进行二分查找,返回大于或等于val的第一个元素位置。如果所有元素都小于val,则返回last的位置, 且last的位置是越界的!

一个数组number序列为:4,10,11,30,69,70,96,100.设要插入数字3,9,111.pos为要插入的位置的下标

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,所以返回最后一个元素的下一个元素)。

 /*手动实现:

该函数当value存在时返回它出现的第一个位置,

如果value不存在则返回位置i,满足在该位置插入value后该序列仍然有序

*/

int lower_bound(int *data, int size, int value)

{

    int mid;

    int left = 0;

    int right = size - 1;

    while(left < right)

    {

        // 确保中点靠近区间的起点

        mid = (right+left)/2;

        // 将中点赋给终点,包含等于情况

        if(data[mid] >= value) right = mid;

        // 将中点加一赋给起点

        else left = mid + 1;

    }

    return left;

}

2. upper_bound(a,a+n,val):返回已升序排好的数组中第一个>val值的位置。如果插入元素大于数组中全部元素,返回的是last。(注意:此时数组下标越界!!)

/*

该函数当value存在时返回它出现的最后一个位置的后面一个位置,因为起点会移动到中点加一

如果value不存在则返回位置i,满足在该位置插入value后该序列仍然有序

*/

int upper_bound(int *data, int size, int value)

{

    int mid;

    int left = 0;

    int right = size - 1;

    while(left < right)

    {

        // 确保中点靠近区间的起点

        mid = left + (right-left)/2;

        // 将中点赋给终点

        if(data[mid] > value) right = mid;

        // 将中点加一赋给起点,包含等于情况

        else left = mid + 1;

    }

    return left;

}

binary_search(数组已经有序)

binary_search(a,a+n,val):首先,数组已经排好序,如果找到val返回true,否则返回false

手动实现:最终返回值为val的位置,但是不一定是最开始或最后的,当序列中存在多个value的时候,bsearch会返回最中间的那一个元素

int binary_search(int *data, int size, int value)

{

    int mid;

    int left = 0;

    int right = size - 1;

    while(left < right)

    {

        // 确保中点靠近区间的起点

        mid = (right+left)/2;

        // 如果找到则返回

        if(data[mid] == value) return mid;

        // 将中点赋给终点

        else if(data[mid] > value) right = mid;

        // 将中点加一赋给起点

        else left = mid + 1;

    }

    return -1;

}

pair #include <utility>

1.pair是一种模板类型,其中包含两个数据值,两个数据的类型可以不同,基本的定义如下:

pair<int, string> a;

 //表示a中有两个类型,第一个元素是int型的,第二个元素是string类型的,如果创建pair的时候没有对其进行初始化,则调用默认构造函数对其初始化。

pair<string, string> a("James", "Joy");//也可以像上面一样在定义的时候直接对其初始化。

2.由于pair类,由于它只有两个元素,分别名为first和second,因此直接使用普通的点操作符即可访问其成员

pair<string, string> a("Lily", "Poly");

string name;

name = pair.second;//用.访问

3. 可以使用make_pair对已存在的两个数据构造一个新的pair类型:

int a = 8;

string m = "James";

pair<int, string> newone;

newone = make_pair(a, m);

3. 在<utility>中已经定义了pair上的六个比较运算符:<、>、<=、>=、==、!=,其规则是先比较first,first相等时再比较second

map

#include<map>

Map是STL的一个关联容器, 可以修改实值,而不能修改key(关键字唯一.)!map里的数据已经根据关键字的字典序排列好(所以说map里作为关键词的数据类型一定要定义好排序规则)

map<KEY , VALUE>

在map中,使用mp[key]查找某个value时,首先要判断map中是否包含key,否则会返回一个默认值(比如0),而这个默认值并不能代表当前map中有这个关键字!

 1.可嵌套:typedef map<TYPE1,map<TYPE2,TYPE3>> map;

2.插入元素:在数据的插入上涉及到集合的唯一性这个概念,即当map中有这个关键  字时,insert操作是插入数据不了的,但是用数组方式就不同了,它可以覆盖以前该关键字对应的值

①.map<string, string> m;

  map<string, string>::iterator mp;

// iterator->first 和 iterator->second 分别代表关键字和存储的数据

  cin >> a>>b; m[a]=b;

②.int i;

m.insert<pair<int,int>(i,i)>;

// map是一个关联容器,里面存放的是键值对,容器中每一元素都是pair类型,通过map的insert()方法来插入元素(pair类型)。

3.查找map中是否包含某个关键字:

    ①.m.find()返回要找的关键字位置

     //用find函数来定位数据出现位置,它返回的一个迭代器,当数据出现时,它返回数据所在位置的迭代器,如果map中没有要查找的数据,它返回的迭代器等于end函数返回的迭代器

    for (mp = m.begin();mp != m.end();mp++)//m.end()返回最后一个元素“”的位置

    {cout << m.find(a)->second; }//输出关键字为a的元素的值

    ②.m.count(关键字)  有关键字返回1,否则返回0

4.清除所有元素:m.clear();

多组数据时一定要注意清除前面的数据

还有就是不能用memset(mp,0,sizeof(mp));

一定要用clear()来清空。

5.清除单个元素

①. //如果要删除关键字1,用迭代器删除

       map<int, string>::iterator iter;

       iter = mapStudent.find(1);

       mapStudent.erase(iter);

       //等价于 mapStudent.erase(mapStudent.find(1));

②.mp=m.begin();m.erase(mp+1);//清除第二个元素

③. //如果要删除1,用关键字删除

       Int n = mapStudent.erase(1);//如果删除了会返回1,否则返回0

6.测试是否为空:m.empty();  为空返回1,否则返回0

7.计算map大小:m.size();//返回大小

8、判断两个map是否相等:mp1==mp2;//当关键字以及关键字对应的值都相等时才相等

unordered_map

1、在c++11以前要使用unordered_map需要

     #include<tr1/unordered_map>//在unordered_map之前加上tr1库名,

     using namespace std::tr1;//与此同时需要加上命名空间

2、无序映射是关联容器,用于存储由键值和映射值组合而成的元素,并允许基于键快速检索各个元素。

在unordered_map中,键值通常用于唯一标识元素,而映射值是与该键关联的内容的对象。键和映射值的类型可能不同。

在内部,unordered_map中的元素没有按照它们的键值或映射值的任何顺序排序,而是根据它们的散列值组织成桶以允许通过它们的键值直接快速访问单个元素(具有常数平均时间复杂度)。

unordered_map容器比映射容器更快地通过它们的键来访问各个元素,尽管它们通过其元素的子集进行范围迭代通常效率较低。

无序映射实现直接访问操作符(operator []),该操作符允许使用其键值作为参数直接访问映射值。

容器中的迭代器至少是前向迭代器。

3、插入、删除数据都和map差不多

[遍历map]

    unordered_map<key,T>::iterator it;

    (*it).first;        //the key value

    (*it).second   //the mapped value

    for(unordered_map<key,T>::iterator iter=mp.begin();iter!=mp.end();iter++)

          cout<<"key value is"<<iter->first<<" the mapped value is "<< iter->second;

4、map和unordered_map的比较

map:

优点:

有序性,这是map结构最大的优点,其元素的有序性在很多应用中都会简化很多的操作

map底层是以红黑树的数据结构实现的,支持的搜索,插入,删除都是O(logn)的时间复杂度。

缺点:

空间占用率高,因为map内部实现了红黑树,虽然提高了运行效率,但是因为每一个节点都需要额外保存父节点、孩子节点和红/黑性质,使得每一个节点都占用大量的空间

unordered_map:

优点: 因为内部实现了哈希表,因此其查找速度非常的快

缺点: 哈希表的建立比较耗费时间

适用处:对于查找问题,unordered_map会更加高效一些,因此遇到查找问题,常会考虑一下用unordered_map

Vector

1 基本操作

(1)头文件#include<vector>.

(2)创建vector对象,vector<int> vec;

(3)尾部插入数字:vec.push_back(a);

(4)使用下标访问元素,cout<<vec[0]<<endl;记住下标是从0开始的。

(5)使用迭代器访问元素.

vector<int>::iterator it;

for(it=vec.begin();it!=vec.end();it++)

    cout<<*it<<endl;

(6)插入元素:    vec.insert(vec.begin()+i,a);在第i+1个(从1开始算)元素前面插入a;

  两个vector拼接:vector<int>a,b;  b接在a后面:a.insert(a.end(),b.begin(),b.end());

(7)删除元素:    vec.erase(vec.begin()+2);删除第3个元素

              vec.erase(vec.begin()+i,vec.end()+j);删除区间[i,j-1];区间从0开始

(8)向量大小:vec.size();

(9)重新设置向量大小:vec.resize(int size,int val);//不写val参数时,将长度改为size,多余的数删除,不足的空位补初始值(0);写val参数的话不足的空位补val

(9)清空:vec.clear();

(10)翻转:reverse(vec.begin(),vec.end());

(11)交换:vec1.swap(vec2);

(11)初始化

vector<int> v2(10); //初始化size为10可以避免数组动态增长的时候不断的分配内存

int  v1[10] = {0,1,0,0,3,0,0,4,4,4};//将v1的数据放到v3

vector<int> v3(&v1[0],&v1[9]);//原始数组的元素指针可以作为迭代器来使用

vector<int> ivec(10,0); //vector<T> v(n,i)形式,v包含n个值为 i 的元素

2

vector的元素不仅仅可以使int,double,string,还可以是结构体,但是要注意:结构体要定义为全局的,否则会出错。

typedef struct rect

{

    int id;

    int length;

    int width;
        //对于向量元素是结构体的,可在结构体内部定义比较函数

    //下面按照id,length,width升序排序。
    bool operator< (const rect &a)  const
         {
            if(id!=a.id)
                   return id<a.id;
        else if(length!=a.length)
                return length<a.length;
        else
                 return width<a.width;
        }
   }Rect;

int main()

{

    vector<Rect> vec;

    Rect rect;

    rect.id=1;

    rect.length=2;

    rect.width=3;

    vec.push_back(rect);

    vector<Rect>::iterator it=vec.begin();

    cout<<(*it).id<<' '<<(*it).length<<' '<<(*it).width<<endl;    

return 0;

}

算法

(1) 使用reverse将元素翻转:需要头文件#include<algorithm>

reverse(vec.begin(),vec.end());将元素翻转

(2)使用sort排序:需要头文件#include<algorithm>,

sort(vec.begin(),vec.end());(默认是按升序排列,即从小到大).

可以通过重写排序比较函数按照降序比较,如下:

定义排序比较函数:

bool Comp(const int &a,const int &b)
    {
    return a>b;
}
调用时:sort(vec.begin(),vec.end(),Comp),这样就降序排序。

如果vector<vector<int> >vec;那么使用sort函数会默认将各数组按从小到大排序,即根据第一个值不同的元素的大小来排

map里也可以嵌套vector:map<vector<int>,int>m;

遍历的时候这样:

for(it=m.begin();it!=m.end();++it){

            vec=it->first;//注意是取first

            for(j=0;j<vec.size();++j){

                printf("%d ",vec[j]);

            }

            printf("\n");

        }

4 注意

当存储的数据超过分配的空间时vector 会重新分配一块内存块,但这样的分配是很耗时的;

内部进行插入、删除操作效率非常低,这样的操作基本上是被禁止的,Vector 被设计成只能在后端进行追加和删除操作;

如果没有指定元素的初始化式,那么标准库将自行提供一个元素初始值进行值初始化。这个由库生成的初始值将用来初始化容器中的每个元素,具体的值为何,取决于存储在vector中元素的数据类型。

对于vector来说,每一次删除和插入,指针都有可能失效,调用push_back在尾部插入也是如此。因为为了保证内部数据的连续存放,iterator指向的那块内存在删除和插入过程中可能已经被其他内存覆盖或者内存已经被释放了。即使时push_back的时候,容器内部空间可能不够,需要一块新的更大的内存,只有把以前的内存释放,申请新的更大的内存,复制已有的数据元素到新的内存,最后把需要插入的元素放到最后,那么以前的内存指针自然就不可用了。特别时在和find等算法在一起使用的时候,牢记原则:不要使用过期的iterator。

vector/set集合-交集,并集,差集,对称差(<algorithm>)

1.set_union求两个集合的并集(删除重复元素的那种,使用前要搞清楚题目怎么定义并集,是否保留重复元素)

int first[] = { 5,10,15,20,25 };

int second[] = { 50,40,30,20,10 };

vector<int> v(10);          //保存结果的地方必须提前开辟内存 0  0  0  0  0  0  0  0  0  0

vector<int>::iterator it;

//使用之前必须排序!!sort使用的cmp参数必须与set_union的一致

sort(first, first + 5);     //  5 10 15 20 25

sort(second, second + 5);   // 10 20 30 40 50

//第4个参数是存放结果的起始地址(地址必须提前开辟内存),还可以加第五个参数cmp

it = set_union(first, first + 5, second, second + 5, v.begin());// 5 10 15 20 25 30 40 50 0 0

    //返回it指向是最后一个数组元素的下一个元素的指针

v.resize(it - v.begin());  // 5 10 15 20 25 30 40 50,重置vector大小

2. set_intersection求交集

函数返回值:结果集合的结束位置的back_insert_iterator(和普通的迭代器不样)。
参数:(第一个集合的开始位置,第一个集合的结束位置,第二个参数的开始位置,第二个参数的结束位置,结果集合的插入迭代器(inserter(result, result.begin()))

插入迭代器一定要先开好内存!

对于第二个算法,Compare指定用于比较元素大小的仿函数。 

3. set_difference求差集:A={1,2,3,4,5},B={1,2,3,6},差集B-A={6}

4. set_symeetric_difference求对称差集:只属于其中一个集合,而不属于另一个集合的元素组成的集合

SET(头文件<set>

set是STL中一种标准关联容器。它底层使用平衡的搜索树——红黑树实现,插入删除操作时仅仅需要指针操作节点即可完成,不涉及到内存移动和拷贝,所以效率比较高。set,顾名思义是“集合”的意思,在set中元素都是唯一的,而且默认情况下会对元素自动进行升序排列,支持集合的交(set_intersection),差(set_difference) 并(set_union),对称差(set_symmetric_difference) 等一些集合上的操作,如果需要集合中的元素允许重复那么可以使用multiset。

访问set中的元素使用迭代器it。*it即为指向的元素

1.创建:

set<int > seta; //默认是小于比较器less<int>的set

set<int, greater<int> > setb; //创建一个带大于比较器的set,需包含头文件functional

int a[5] = {1,2,3,4,5};

set<int > setc(a,a+5); //数组a初始化一个set;

set<int > setd(setc.begin(),setc.end()); //setc初始化一个set

 //上述两例均为区间初始化

  set<int > sete(setd); //拷贝构造创建set

2.插入:set的插入删除操作不会影响之前的迭代器指针

s.insert(1);//插入数字1,但是同一个数只能插入一次

int a[4] = {11,12,13,14};

s.insert(a,a+4); //将区间[a, a+4]里的元素插入容器

3.删除:

s.erase(9); //根据元素删除,即删除里面的数字9

set<int>::iterator ita = s.begin();

s.erase(ita);  //删除迭代器指向位置的元素

ita = s.begin();

    itb = s.begin();

    itb++;itb++;

    s.erase(ita,itb); //删除区间[ita,itb)的元素

s.clear()       删除set容器中的所有的元素

4.查找:

s.find()        查找一个元素,如果容器中不存在该元素,返回值等于s.end()

s.count(1));   返回1的个数

5.其他:

1)s.lower_bound() 返回第一个大于或等于给定关键值的元素

2)s.upper_bound() 返回第一个大于给定关键值的元素

3)  s.equal_range() 返回一对定位器,分别表示 第一个大于或等于给定关键值的元素 和 第一个大于给定关键值的元素,这个返回值是一个pair类型(例如:*s.equal_range(2).first,*s.equal_range(2).second),如果这一对定位器中哪个返回失败,就会等于s.end()

4)s.empty()判断是否为空

5)自定义比较函数

struct cmp{

    bool operator () (const int &a, const int &b){

        return a > b;

    }

};

set<int, cmp>s; //自定义排序函数构造set,按降序排

//用结构体的时候一定要重载运算符

struct Info

{

    string name;

    double score;

    bool operator < (const Info &a) const // 重载“<”操作符,自定义排序规则

    {

        //按score由大到小排序。如果要由小到大排序,使用“>”即可。

        return a.score < score;

    }

};

set<Info> s;

queue(队列,先进先出):

(一)基本知识

#include<queue>

q.empty():判断队列是否为空,队列q空时,返回true,否则返回 false

q.size():访问队列q中的元素个数

q.push(x):将x元素接到队列的末端;

q.pop() 弹出队列的第一个元素,并不会返回元素的值;

q.front():返回队列q内的第一个元素(即第一个被置入的元素)

q.back():返回队列q中最后一个元素(即最后被插入的元素)

queue<Type>M;//例:queue<int>Q; 队列Q中的元素都是int型

(二)priority_queue(优先队列)

优先队列与队列的差别在于优先队列不是按照入队的顺序出队,而是按照队列中元素的优先权顺序出队(默认为大者优先,也可以通过指定算子来指定自己的优先顺序)。在最小优先队列(min priorityq u e u e)中,查找操作用来搜索优先权最小的元素,删除操作用来删除该元素;对于最大优先队列(max priority queue),查找操作用来搜索优先权最大的元素,删除操作用来删除该元素.优先权队列中的元素可以有相同的优先权,查找与删除操作可根据任意优先权进行.

返回首元素时用q.top();

技巧:>,大于,greater,递增,最小值优先;<,小于,less,递减,最大值优先

priority_queue模版类有三个模版参数,元素类型,容器类型,比较算子。

①: 默认容器为vector,默认算子为less

priority_queue<int>que;//采用默认优先级构造队列

②:定义算子,使用运算符重载,自定义优先级

struct cmp{  

    bool operator ()(int &a,int &b){  

        return a>b;//最小值优先

      或return a<b;//最大值优先   

    }  

};

priority_queue<int,vector<int>,cmp>que; 

③:定义元素类型,使用运算符重载,自定义优先级  

struct number { 

    int x;  

    bool operator < (const number &a) const {  

        return x>a.x;//最小值优先

      或return x<a.x;//最大值优先   

    }  

};  

priority_queue<number>que;

stack<stack>  

1.声明:stack<int> mystack;

2.mystack.empty()

Test whether container is empty ,return true if size=0,false otherwise

2.mystack.size()

Return size, the number of elements in the stack, an unsigned integral type.

3. mystack.top()

Returns a reference to the top element in the stack

4.push()

insert element,void push (const value_type& val);

5.emplace()

Construct and insert element.

push()函数和emplace()都是在栈这个容器的顶部插入一个新的元素。

push(),实际上是调用的底层容器的push_back()函数,新元素的值是push函数参数的一个拷贝。

emplace(),实际上是调用的底层容器的emplace_back()函数,新元素的值是在容器内部就地构造的,不需要移动或者拷贝。

stack<string> mystack;

mystack.emplace ("First sentence");

6.pop()

Remove top element

7.swap()

交换两个栈的内容(所有元素),这个函数通过非成员函数swap()来交换底层容器,时间复杂度O(1).

foo.push (10); foo.push(20); foo.push(30);

  bar.push (111); bar.push(222);

  foo.swap(bar);//此时foo.size=2,bar.size=3,且内容交换了

应用一:括号匹配

#include<stack>

bool match(char exp[]) {

stack<char> s;

char *p, c;

for (p = exp;*p != '\0';++p) {//遍历整个表达式

if (*p == '(' || *p == '{' || *p == '[')//左括号直接压栈

s.push(*p);

else if (*p == ')' || *p == ']' || *p == '}') {//右括号进行匹配

if (s.empty())//没有可以匹配的话返回失败信息

return false;

else {//否则判断匹配是否正确

c = s.top();

s.pop();

if (*p == ')'&&c != '(')return false;

if (*p == '}'&&c != '{')return false;

if (*p == ']'&&c != '[')return false;

}

}

}

if (!s.empty())//最后栈非空的话返回错误信息

return false;

return true;//否则正确返回

}

Heap

stl中的堆默认是最大堆,要想用最小堆的话,必须要在push_heap,pop_heap,make_heap等每一个函数后面加第三个参数greater<int>(),括号不能省略。#include <algorithm>  #include <vector> 

1、make_heap:使序列变成堆

2、push_heap:压栈(入栈)

3、pop_heap:弹栈(出栈)。堆顶元素与序列最后元素交换

4、sort_heap:对堆排序

例子:

int myints[] = {10,20,30,5,15};

  vector<int> v(myints,myints+5);

  vector<int>::iterator it;

 

  make_heap (v.begin(),v.end());

  cout << "initial max heap   : " << v.front() << endl;// 30

 

  pop_heap (v.begin(),v.end()); v.pop_back();

  cout << "max heap after pop : " << v.front() << endl;// 20

 

  v.push_back(99); push_heap (v.begin(),v.end());

  cout << "max heap after push: " << v.front() << endl;// 99

 

  sort_heap (v.begin(),v.end());

 

  cout << "final sorted range :";

  for (unsigned i=0; i<v.size(); i++) cout << " " << v[i];   // 5 10 15 20 99

5、手敲:

void HeapAdjust(int s, int m) {//从s到m重新调整成为 小顶堆

int t = heap[s];//保存要可能要调整到下面的元素值

int i;

for (i = s * 2; i <= m; i *= 2) {//从第一个元素的孩子辈开始向下调整

if (i < m&&heap[i + 1] < heap[i])//选出左右孩子中更小的那个

i++;

if (t <= heap[i])break;//直到找到比要调整的元素大的位置,停止向下调整

heap[s] = heap[i];//没有找到的话,说明还要继续向下找,而更小的元素要调整到上层

s = i;

}

heap[s] = t;//最终找到合适的位置

}

两种建堆方式,建出来来的结果会不一样:

①将一个数组里的数调整成堆

for (i = 1; i <= n; ++i)

   cin>>heap[i];

for (i = n / 2; i >= 1; --i)

   HeapAdjust(i, n);

②将一系列给定数字顺序插入一个初始为空的小顶堆H[]

for (i = 1; i <= n; ++i) {

cin>>heap[i];

for (j = i / 2; j >= 1; --j)

    HeapAdjust(j, i);

//将一系列给定数字顺序插入一个初始为空的小顶堆H[]!

//意思是,输入一个数,插入一次。。

}

List(双向链表)(可以自己看看)

C++ List(双向链表)  <list>

list 的特点:

(1) 不使用连续的内存空间这样可以随意地进行动态操作;

(2) 可以在内部任何位置快速地插入或删除,当然也可以在两端进行push和pop 。  

(3) 不能进行内部的随机访问,即不支持[ ] 操作符和vector.at() ;  Lists将元素按顺序储存在链表中,与向量(vectors)相比,它允许快速的插入和删除,但是随机访问却比较慢.  

1.assign() 给list赋值  

语法:  void assign( input_iterator start, input_iterator end );  

//以迭代器start和end指示的范围为list赋值  

void assign( size_type num, const TYPE &val );  //赋值num个以val为值的元素。  

2.back() 返回最后一个元素的引用  

3.begin() 返回指向第一个元素的迭代器  

4.clear() 删除所有元素  

5.empty() 如果list是空的则返回true  

6.end() 返回末尾的迭代器  

7.erase() 删除一个元素  !!但是注意,删除之后的迭代器不一定指向之后的元素,在遍历集合删除多个元素的时候一定要注意不能直接iterator++

语法: iterator erase( iterator loc );//删除loc处的元素  

iterator erase( iterator start, iterator end ); //删除start和end之间的元素  

8.front() 返回第一个元素的引用  

9.get_allocator() 返回list的配置器  

10.insert() 插入一个元素到list中  

语法:iterator insert( iterator loc, const TYPE &val );

 //在指定位置loc前插入值为val的元素,返回指向这个元素的迭代器,

void insert( iterator loc, size_type num, const TYPE &val );  

//定位置loc前插入num个值为val的元素  

void insert( iterator loc, input_iterator start, input_iterator end );  

//在指定位置loc前插入区间[start, end)的所有元素  

11.max_size() 返回list能容纳的最大元素数量  

12.merge() 合并两个list  

语法: void merge( list &lst );//把自己和lst链表连接在一起  

void merge( list &lst, Comp compfunction );  //指定compfunction,则将指定函数作为比较的依据。  13.pop_back() 删除最后一个元素  

14.pop_front() 删除第一个元素  

15.push_back() 在list的末尾添加一个元素

16.push_front() 在list的头部添加一个元素  

17.rbegin() 返回指向第一个元素的逆向迭代器  

18.remove() 从list删除元素  

语法:  void remove( const TYPE &val );  //删除链表中所有值为val的元素  

19.remove_if() 按指定条件删除元素  

20.rend() 指向list末尾的逆向迭代器  

21.resize() 改变list的大小  

语法:  void resize( size_type num, TYPE val );  

//把list的大小改变到num。被加入的多余的元素都被赋值为val

 22.reverse() 把list的元素倒转  

23.size() 返回list中的元素个数(注意:可能会是O(N)的时间复杂度)

24.sort() 给list排序  

语法:  void sort();//为链表排序,默认是升序  

void sort( Comp compfunction );//采用指定函数compfunction来判定两个元素的大小。  

25.splice() 合并两个list  语法:  void splice( iterator pos, list &lst );//把lst连接到pos的位置  

void splice( iterator pos, list &lst, iterator del );//插入lst中del所指元素到现链表的pos上  

void splice( iterator pos, list &lst, iterator start, iterator end );//用start和end指定范围。  

26.swap() 交换两个list  

语法:  void swap( list &lst );// 交换lst和现链表中的元素  

27.unique() 删除list中重复的元素  

语法:  void unique();//删除链表中所有重复的元素  

void unique( BinPred pr );// 指定pr,则使用pr来判定是否删除。

去重函数Unique(结合erase)

一、概述

unique函数属于STL中比较常用函数(algorithm中),它的功能是元素去重。即”删除”序列中所有相邻的重复元素(只保留一个)。此处的删除,并不是真的删除,而是指重复元素的位置被不重复的元素给占领了(用后面不重复的元素代替前面重复的元素)。由于它”删除”的是相邻的重复元素,所以在使用unique函数之前,一般都会将目标序列进行排序。

二、原型

unique函数的函数原型如下:

1.只有两个参数,且参数类型都是迭代器:

iterator unique(iterator it_1,iterator it_2);

这种类型的unique函数是我们最常用的形式。其中这两个参数表示对容器中[it_1,it_2)范围的元素进行去重(注:区间是前闭后开,即不包含it_2所指的元素),返回值是一个迭代器,它指向的是去重后容器中不重复序列的最后一个元素的下一个元素。

2.有三个参数,且前两个参数类型为迭代器,最后一个参数类型可以看作是bool类型:

iterator unique(iterator it_1,iterator it_2,bool MyFunc);

该类型的unique函数我们使用的比较少,其中前两个参数和返回值同上面类型的unique函数是一样的,主要区别在于第三个参数。这里的第三个参数表示的是自定义元素是否相等。也就是说通过自定义两个元素相等的规则,来对容器中元素进行去重。这里的第三个参数与STL中sort函数的第三个参数功能类似

示例:

static bool myfunc(int i, int j)//可以用自定义的相等

{

    return (i + 1) == j;

    //return i == j;

}

int main()

{

    vector<int> a = {1,3,3,4,5,6,6,7};

    vector<int>::iterator it_1 = a.begin();

    vector<int>::iterator it_2 = a.end();

 

    //sort(it_1,it_2);//一般在使用前是需要去重的,但是不是必须去重(根据你的情况来)

    //unique(it_1,it_2,myfunc);

    unique(it_1,it_2);

    //去重后:13456767

}

三、结合erase实现真正的去重

unique函数通常和erase函数一起使用,来达到删除重复元素的目的。(注:此处的删除是真正的删除,即从容器中去除重复的元素,容器的长度也发生了变换;而单纯的使用unique函数的话,容器的长度并没有发生变化,只是元素的位置发生了变化)

vector<int> a ={1,3,3,4,5,6,6,7};

    vector<int>::iterator it_1 = a.begin();

    vector<int>::iterator it_2 = a.end();

    vector<int>::iterator new_end;

 

    new_end = unique(it_1,it_2); //注意unique的返回值是一个指向器

    a.erase(new_end,it_2);//134567,已经删除了最后的数字

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值