算法中的C++入门及STL

​​​​​​​

目录

1.C++入门

1.1.常用头文件

1.2.C++命名空间

1.3.输入输出

1.3.1.输入std::cin(cin) 

1.3.2.输出std::cout(cout) 

1.3.3.换行std::endl(endl) 

1.4.C++结构体

1.4.1.结构体声明

1.4.2.结构体重载

1.4.3.结构体函数

1.4.3.1构造函数

1.4.3.1.1.参数全初始化

1.4.3.1.2.参数部分初始化

1.4.3.1.3.简便写法

1.4.3.2.结构体中的其他函数

2.STL

2.1.容器

2.2.迭代器

2.3.vector

2.3.1.功能

2.3.2.vector和普通数组的区别:

2.3.3.动态扩展

2.3.4.vector初始化

2.3.5.常用函数

2.3.6.重载符号

2.4.array

2.5.set

2.5.1.set初始化

2.5.2.常用函数

2.6.map

2.6.1.map初始化

2.6.2.map访问

2.6.2.1.下标

2.6.2.2.迭代器

2.6.3.常用函数

2.6.3.1.find()

2.6.3.2.erase()

2.6.3.2.1. 删除单个元素

2.6.3.2.2.删除一个区间内所有的元素

2.6.3.3.size()和clear()

2.7.multiset和multimap

2.8.unordered_set和unordered_map

2.8.1.unordered_set

2.8.2.unordered_map

2.9.stack和queue

2.9.1.stack

2.9.1.1.声明

2.9.1.2.常用操作

2.9.2.queue

2.9.2.1.声明

2.9.2.2.常用函数

2.10.priority_queue

2.10.1.声明

2.10.2.常用操作

3.string类

3.1.初始化

3.2.输入输出

3.2.1.用cin输入

3.2.2.用getline读取一整行

3.3.比较string的大小

3.4.字符串的加法

3.4.1.两个string对象相加

3.4.2.string对象加上一个字符(或字符串)字面值

3.4.2.1.例子

3.4.2.2.易错点

3.5.获取和处理string中的每个字符

3.5.1.类似于字符数组,使用下标访问[]

3.5.2.使用迭代器访问

3.5.3.使用基于范围的for语句

3.6.string类型的相关方法

3.6.1.substr成员函数

3.6.2.string对象的insert()

3.6.2.1.迭代器

3.6.2.2.下标

3.6.2.2.1. insert(Index,count,ch)

3.6.2.2.2.insert(index,s)

3.6.2.2.3.insert(index,str,index_str,count)

3.6.2.2.4.insert(index,s,count)

3.6.3.string对象的erase()

3.6.4.字符串和整形间的转换

3.6.5.搜索函数find()

4.STL库常用算法

4.1.快速排序std::sort

4.2.二分查找

4.3.交换std::swap()

4.4.std::fill()

5.基于范围的for循环(C++11起)

5.1.理解

5.2.语法

5.3.使用

6.复杂度

6.1.时间复杂度

6.2.空间复杂度


1.C++入门

1.1.常用头文件

#include<bits/stdc+.h> 
//俗称万能头。优点:包含常用的所有头文件,在算法竞赛中推荐使用,因为不会考虑编译时间。缺点:由于包含了很多头文件,在同时使用using namespace std;的情况下,部分变量名不能使用,因为与std::Xxx重名。
#include<iostream> 
#include<algorithm> 
#include<vector> 
#include<string> 
#include<map> 
#include<set> 
#include<queue> 
#include<deque> 
#incude<stack> 
#include<iterator> 
#include<cmath> 
#include<cstdio> 
#include<cstring> 
#include<cstdlib>

1.2.C++命名空间

question:为什么会有命名空间?

answer:简单来说,为了在复杂工程中避免重名问题,命名空间将标识符进行了本地化,不会与头文件中全局变量的名字进行冲突。

算法竞赛中,通常不会使用命名空间相关知识,但有时会大量使用标佳库内容,所以通常加上using namespace std;

#include<bits/stdc++.h>
int main() {
	int a, b;
	std::cin >> a >> b;
	std::cout << a << " " << b << endl;
	return 0;
}
#include<bits/stdc++.h>
using namespace std;
int main() {
	int a, b;
	cin >> a >> b;
	cout << a << " " << b << endl;
	return 0;
}

1.3.输入输出

C++的输入与输入是通过流(stream)来实现的(通过命名空间std,如果没有using namespace std,输入输出换行需要写在前加上std::),我们常用的是预定义标准流对象中的std::cin与std::cout以及std:.endl; 

1.3.1.输入std::cin(cin) 

#include<bits/stdc++.h>
using namespace std;
int main() {
	int a, b;
	cin >> a >> b;
	return 0;
}

1.3.2.输出std::cout(cout) 

#include<bits/stdc++.h>
using namespace std;
int main() {
	int a = 0;
	cout << a;
	return 0;
}

1.3.3.换行std::endl(endl) 

#include<bits/stdc++.h>
using namespace std;
int main() {
	int a, b;
	cin >> a >> b;
	cout << a << " " << b << endl;
	return 0;
}

优点:相比于scanf与printf。std::cin与std::cout无需指定类型即可输入与输出。
缺点:1.由于兼顾c语言的输入输出方式,在不使用解绑的情况下,速度比scanf与printf 相比会慢上许多。(解绑函数:ios::sync_with_stdio(false);cin.tie(0);cout.tie(O))2.在一些题目中要求特殊格式的输出时,cout不比printf方便。

1.4.C++结构体

C++的结构体与c语言中的结构体形式基本一样,区别在于C++对结构体进行了拓展,其功能基本等同于C++的类(Class),因此,我们可以重载结构体的运算符,在结构体内编写函数,可以直接用结构体名声明一个新的结构体变量,这些在以前的c语言是无法做到的。

1.4.1.结构体声明

struct node {
	int a, b;
};
//声明结构体变量
struct node x;
node y;//C语言只能使用上一个方法

1.4.2.结构体重载

我们可以通过多种形式重载结构体内的运算符,下面给出推荐的一种形式

struct node
{
    int u, v, w;
    bool operator < (const node &x)const{
        return w > x.w;//也可以换成this->w > x.w;
    }
};

其代表的意思是,如果当前结构体的w>结构体x的w,那么则当前结构体<结构体x。
重载结构体的运算符可以在需要实现结构体排序,或在作为一些STL容器的变量类型时非常有用。

1.4.3.结构体函数

1.4.3.1构造函数
1.4.3.1.1.参数全初始化

this指针用来指出第一个val是结构体的成员变量val,this可以不写,写了更清晰

struct Node {
    int val;
    Node* next;
//默认构造函数 Node(){} C++自带,我们不需要显式地写出来
    Node(int val,Node* p){
        this->val = val;
        this->next = p;
    }
};
/*struct Node {
    int val;
    Node* next;
    Node(int v,Node* p){
        val = v;
        next = p;
    }
};*/
1.4.3.1.2.参数部分初始化
#include<iostream>
using namespace std;
struct Node {
    int val;
    Node* next;
    Node(int v) {
        val = v;
    }
};
int main() {
    Node head(10);
    Node node = { 2 };
    (&head)->next = &node;
    cout << head.val << endl << (&head)->next->val << endl << node.val << endl;
    return 0;
}
/*运行结果:
10
2
2*/

[注] 如果构造函数只写了部分参数,在实例化时会比较麻烦,一是实例化时不能不带参数,也不能用成员列表把所有参数都写上去,只能写你在构造函数时限定的部分参数。

1.4.3.1.3.简便写法
#include<iostream>
using namespace std;
struct Node {
    int val;
    Node* next;
    //默认构造函数
    /*Node() {}*/
    Node() :val(), next() {}
    /*Node(int v) {
        val = v;
    }*/
    Node(int val) :val(val) {}
};
int main()
{
    Node head(10);
    Node node = { 2 };
    (&head)->next = &node;
    cout << head.val << endl << (&head)->next->val << endl << node.val << endl;
    return 0;
}
/*运行结果:
10
2
2*/
1.4.3.2.结构体中的其他函数
#include<iostream>
using namespace std;
struct Node {
    int val;
    Node* next;
    /*Node() {}*/
    Node(int val) :val(val) {}
    int add(int a, Node* x) {
        return a + x->val;
    }
};
int main()
{
    Node head(10);
    Node node = { 2 };
    (&head)->next = &node;
    cout << head.add(head.val, (&head)->next);
    return 0;
}
//输出结果12

2.STL

STL即标准模板库(Standard Template Library) ,是C++标准库的一部分,里面包含了一些模板化的通用的数据结构和算法。由于其模板化的特点,它能够兼容自定义的数据类型,避免大量的造轮子工作。NOI和ICPC赛事都支持STL库的使用,因此合理利用STL可以避免编写无用算法,并且充分利用编译器对模板库优化提高效率。

2.1.容器

顺序容器:

array(C++11):静态的连续数组;

vector:动态的连续数组;

deque:双端队列;

forward_list(C++11):单链表;

list:双链表。

关联容器(默认升序):

set:唯一键的集合,按键排序;

map:键值对的集合,按键排序,键是唯一的;

multiset:键的集合,按键排序;

multimap:键值对的集合,按键排序。

无序关联容器:

unordered_set:唯一键的集合,按键生成散列;

unordered_map:键值对的集合,按键生成散列,键是唯一的;

unordered_multiset:键的集合,按键生成散列;

unordered_multimap:键值对的集合,按键生成散列。

容器适配器:

stack:适配一个容器以提供栈(LIFO数据结构);

queue:适配一个容器以提供队列(LIFO数据结构);

priority_queue:适配一个容器以提供优先级队列。

2.2.迭代器

迭代器是一种广义化的指针,它使得C++程序可以通过统一的方式处理不同的数据结构(例如容器和范围(C++20起)。迭代器库提供了迭代器的定义,同时还提供了迭代器特征、适配器及相关的工具函数。因为迭代器是指针的抽象,所以它们的语义是C++的指针的大多数语义的泛化。这确保指针能够用于所有接受迭代器的函数模板。(理解为指针)

#include<bits/stdc++.h>
using namespace std;
int main() {
    vector<int> v;
    v.push_back(2);//v:{2}
    v.push_back(0);//v:{2, 0}
    v.push_back(1);//v:{2, 0, 1}
    vector<int>::iterator it;//迭代器的声明
    for (it = v.begin(); it != v.end(); it++) {
        cout << *it << endl;
    }//v.end()是v容器最后一个元素的再后一个
    return 0;
}
/*运行结果:
2
0
1*/

2.3.vector

2.3.1.功能

vector容器的功能和数组非常相似,使用时可以把它看成一个数组

2.3.2.vector和普通数组的区别:

1.数组是静态的,长度不可改变,而vector可以动态扩展,增加长度
2.数组内数据通常存储在栈上,而vector中数据存储在堆上

2.3.3.动态扩展

动态扩展并不是在原空间之后续接新空间,而是找到比原来更大的内存空间,将原数据拷贝到新空间,释放原空间

2.3.4.vector初始化

1.vector<数据类型> 名;
2.vector<数据类型> 名(大小);
3.vector<数据类型> 名(大小,初始值);

vector容器大小:

resize(int num)//重新指定容器的长度为num,若容器变长,则以默认值0填充新位置,如果容器变短,则末尾超过容器长度的元素被删除

vector<int> x;
//使用reszie
x.resize(5);

vector预留空间

reserve(int len);//容器预留len个元素长度,也就是把容量扩为len,预留的位置并不初始化,同时也不可访问

vector<int> x;
//使用reserve
x.reserve(5);

2.3.5.常用函数

1.push_back(elm); //尾部插入元素elm
2.pop_back(); //删除最后一个元素
3.insert(const_iterator pos,ele); //在迭代器指向的位置pos前插入一个元素ele
4.insert(const_iterator pos,int count,ele); //在迭代器指向的位置pos处插入count个元素ele
5.erase(const_iterator pos); //删除迭代器指向的元素
6.erase(const_iterator begin,const_iterator end); //删除迭代器区间[begin,end)之间的元素
7.clear(); //删除容器中所有元素
8.front(); //返回容器中第一个元素
9.back(); //返回容器中最后一个元素
10.size(); //返回容器的大小,即容器中元素的个数
11.empty(); //判断容器是否为空,为空返回1,否则返回0
12.swap(v); //当前容器与容器v交换,同时v被清空
13.begin(); //正向起始迭代器
14.end(); //正向终点迭代器,指向的是最后一个元素的后面
14.rend(); //反向终点迭代器
14.rbegin(); //反向起始迭代器

#include<bits/stdc++.h>
using namespace std;
int main() {
    vector<int> v;
    v.push_back(2);//v:{2}
    v.push_back(0);//v:{2, 0}
    v.push_back(1);//v:{2, 0, 1}
    /*push_back(int n)实际上是在v.end()处插入元素n
   如果vector<int> v(2,0);//初始大小为2,值为0
    v.push_back(2);//v:{0, 0, 2}
    v.push_back(0);//v:{0, 0, 2, 0}
    v.push_back(1);//v:{0, 0, 2, 0, 1}*/
    v.pop_back();//v:{2, 0}
    v.insert(v.begin(), 5);//v:{5 ,2, 0}
    v.insert(v.begin(), 2, 5);//v:{5, 5, 5 ,2, 0}
    v.erase(v.begin());//v:{5, 5 ,2, 0}
    v.erase(v.begin(), v.end() - 2);//v:{2, 0}
    int t = v.front();//t = 2
    int o = v.back();//o = 0
    int x = v.size();//x = 2
    int f;
    f = v.empty();//不为空 f = 0
    v.clear();//v:{}
    vector<int> s;
    s.push_back(5);//s:{5}
    s.push_back(4);//s:{5, 4}
    s.push_back(3);//s:{5, 4, 3}
    v.swap(s);//v:{5, 4, 3},s:{}
    f = v.empty();//为空 f = 1
    return 0;
}

2.3.6.重载符号

=号,vector<int>v1,v2; v1 = v2//让v2赋值给v1
==号,v1==v2,按照字典序判断v1是否与v2相等,不相等就为!=
>号,v1>v2,按照字典序判断v1是否大于v2,位数不够补零
<号,v1>v2,按照字典序判断v1是否小于v2,位数不够补零

2.4.array

array即是数组,可以认为是对c语言中静态数组的封装其声明形式为array<数据类型,长度> name;

算法中不常用array,不做解释。

2.5.set

set是关联容器,含有Key类型对象的已排序集。用比较函数比较(Compare)进行排序。搜索、移除和插入拥有对数复杂度。set通常以红黑树实现。
这句话是指,set中的元素唯一,并且是有序状态,初始默认升序,搜索,删除,插入复杂度均为O(logn);

2.5.1.set初始化

set<数据类型> 名;//默认升序
set<type,greater<数据类型> > 名;//降序
set<type,less<数据类型> > 名;升序

2.5.2.常用函数

set<type> s;
s.insert(val);//插入一个值→set.insert(begin,end);插入另一块容器区间[begin,end)的值。
s.count(val);/查询set中值为val的数有多少,返回o和1。
s.erase(val);//删除值为val的元素
s.erase(it);//删除迭代器it指向元素
s.erase(begin,end);//删除迭代器区间[begin,end)的元素

#include<bits/stdc++.h>
using namespace std;
int main() {
	set<int> s;
	s.insert(5);//s:5 
	s.insert(1);//s:1 5
	s.insert(7);//s:1 5 7
	s.insert(3);//s:1 3 5 7
	s.insert(6);//s:1 3 5 6 7
	s.insert(5);//重复的元素不会插入,set的键值唯一,s:1 3 5 6 7
	for (auto a : s) {
		cout << a << ' ';
	}
	cout << endl;
	cout << s.count(1) << endl;//输出1
	cout << s.count(0) << endl;//输出0
	s.erase(5);//s:1 3 6 7
	for (auto a : s) {
		cout << a << ' ';
	}
	cout << endl;
	s.erase(s.begin());//s:3 6 7
	for (auto a : s) {
		cout << a << ' ';
	}
	cout << endl;
	s.erase(s.begin(), --s.end());//s:7
	for (auto a : s) {
		cout << a << ' ';
	}
	cout << endl;
    return 0;
}
/*运行结果:
1 3 5 6 7
1
0
1 3 6 7
3 6 7
7*/

2.6.map

std::map是有序键值对容器,它的元素的键是唯一的。用比较函数Compare排序键。搜索、移除和插入操作拥有对数复杂度。map 通常实现为红黑树.

2.6.1.map初始化

map<数据类型1,数据类型2> 名;数据类型1为键值key,数据类型2为映射val

2.6.2.map访问

2.6.2.1.下标

和普通数组一样,例如一个定义为map<char, int> mp的map来说,可以直接使用mp['c']的方式来访问它对应的int整数。可以直接使用mp['c'] = 20这样的方式来赋值

map<char, int> mp; 
mp['c'] = 20;
cout << mp['c'];	//答案输出20

但是要注意的是,map中的键是唯一的,例如以下代码:

map<char, int> mp; 
mp['c'] = 20;
mp['c'] = 30;	//30覆盖了20 
mp['c'] = 666; 	//666覆盖了30 
cout << mp['c'];	//答案输出666 
2.6.2.2.迭代器

map迭代器的定义和其他STL容器迭代器定义的方式相同;map会以键从小到大的顺序自动排序

#include<bits/stdc++.h>
using namespace std;
int main() {
	map<char, int> mp;
	mp['c'] = 222;
	mp['a'] = 333;
	mp['b'] = 444;
	for (map<char, int>::iterator it = mp.begin(); it != mp.end(); it++) {
		cout << it->first << " " << it->second << endl;
	}
	return 0;
}
/*运行结果:
a 333
b 444
c 222*/

2.6.3.常用函数

2.6.3.1.find()

find(key);//返回键为key映射的迭代器,时间复杂度为O(logN),N为map中映射的个数

#include<bits/stdc++.h>
using namespace std;
int main() {
	map<char, int> mp;
	mp['a'] = 222;
	mp['b'] = 333;
	mp['c'] = 444;
	map<char, int>::iterator it = mp.find('b');
	cout << it->first << " " << it->second;
	return 0;
}
/*运行结果:
b 333*/
2.6.3.2.erase()
2.6.3.2.1. 删除单个元素

mp.erase(it),it为需要删除的元素的迭代器。时间复杂度为O(1)
mp.erase(key),key为要删除的映射的键。时间复杂度O(logN),N为map内元素的个数:

#include<bits/stdc++.h>
using namespace std;
int main() {
	map<char, int> mp;
	mp['a'] = 222;
	mp['b'] = 333;
	mp['c'] = 444;
	map<char, int>::iterator it = mp.find('b');
	mp.erase(it);
	for (map<char, int>::iterator it = mp.begin(); it != mp.end(); it++) {
		cout << it->first << " " << it->second << endl;
	}
	cout << endl;
	mp.erase('c');
	for (map<char, int>::iterator it = mp.begin(); it != mp.end(); it++) {
		cout << it->first << " " << it->second << endl;
	}
	return 0;
}
/*运行结果:
a 222
c 444

a 222*/
2.6.3.2.2.删除一个区间内所有的元素

mp.erase(first, last),其中,first为需要删除的区间的起始迭代器,last为需要删除的区间末尾迭代器的下一个地址,即为

删除左闭右开的区间[first, last)。时间复杂度为O(last - first)

#include<bits/stdc++.h>
using namespace std;
int main() {
	map<char, int> mp;
	mp['a'] = 222;
	mp['b'] = 333;
	mp['c'] = 444;
	map<char, int>::iterator it = mp.find('b');
	mp.erase(it, mp.end()); //删除it之后的所有映射,即b 333和 c 444 
	for (map<char, int>::iterator it = mp.begin(); it != mp.end(); it++) {
		cout << it->first << " " << it->second << endl;
	}
	return 0;
}
/*运行结果:
a 222*/
2.6.3.3.size()和clear()

1.size()用来获得map中映射的对数,复杂度为O(1);
2.clear()用来清空map中的所有元素,复杂度为O(N),N为map中元素个数。

#include<iostream>
#include<map>
using namespace std;
int main(){
	map<char, int> mp; 
	mp['a'] = 222;
	mp['b'] = 333;	
	mp['c'] = 444;
	mp.clear();	//清空map
	cout << mp.size(); 
	return 0;
}
//运行结果:0

2.7.multiset和multimap

multiset和lmultimap均为key值可以重复的set和map,由于实际用。处很少,再次不做叙述有兴趣可以看multimapmultiset

2.8.unordered_set和unordered_map

2.8.1.unordered_set

unordered_set是含有Key类型唯─对象集合的关联容器。搜索、插入和移除拥有平均常数时间复杂度。在内部,元素并不以任何特别顺序排序,而是组织进桶中。元素被放进哪个桶完全依赖其值的哈希。这允许对单独元素的快速访问,因为哈希一旦确定,就准确指代元素被放入的桶。

2.8.2.unordered_map

unordered_map是关联容器,含有带唯一键的键-值pair。搜索、插入和元素移除拥有平均常数时间复杂度。元素在内部不以任何特定顺序排序,而是组织进桶中。元素放·进哪个桶完全依赖于其键的哈希。这允许对单独元素的快速访问,因为一旦计算哈希,则它准确指代元素所放进的桶。

2.9.stack和queue

2.9.1.stack

栈是基本的数据结构之一,特点是先进后出,就如开进死胡同的车队,先进去的只能最后出来.

2.9.1.1.声明

stack<数据类型> 名;

2.9.1.2.常用操作

q.push(x);        //将x压入栈顶
q.top();        //返回栈顶的元素
q.pop();        //删除栈顶的元素
q.size();        //返回栈中元素的个数
q.empty();        //检查栈是否为空,若为空返回true,否则返回false

#include<bits/stdc++.h>
using namespace std;
int main() {
	stack<int> q;
	q.push(5);//q:5 栈顶5
	q.push(4);//q:5 4 栈顶4
	q.push(3);//q:5 4 3 栈顶3
	q.push(2);//q:5 4 3 2 栈顶2
	q.push(1);//q:5 4 3 2 1 栈顶1
	cout << "top:" << q.top() << endl;
	q.pop();//q:5 4 3 2 栈顶2
	cout << "size:" << q.size() << endl;
	cout << q.empty();
	return 0;
}
/*运行结果:
top:1
size:4
0*/

2.9.2.queue

2.9.2.1.声明

queue<数据类型> 名;

2.9.2.2.常用函数

q.push(x);        //将x放入队列
q.front();         //返回队首元素
q.pop();        //删除队首元素
q.back();        //返回队尾元素
q.size();        //返回q中的元素个数
q.empty();        //检查队列是否为空,若为空返回true,否则返回false

#include<bits/stdc++.h>
using namespace std;
int main() {
	queue<int> q;
	q.push(5);//q:5 队首5 队尾5
	q.push(4);//q:5 4 队首5 队尾4
	q.push(3);//q:5 4 3 队首5 队尾3
	q.push(2);//q:5 4 3 2 队首5 队尾2
	q.push(1);//q:5 4 3 2 1 队首5 队尾1
	cout << "front:" << q.front() << endl;
	cout << "back:" << q.back() << endl;
	q.pop();//q:4 3 2 1 队首4 队尾1
	cout << "front:" << q.front() << endl;
	cout << "back:" << q.back() << endl;
	cout << "size:" << q.size() << endl;
	cout << q.empty();
	return 0;
}
/*运行结果:
front:5
back:1
front:4
back:1
size:4
0*/

2.10.priority_queue

2.10.1.声明

priority_queue<数据类型> 名; //默认大到小排序

priority_queue<数据类型,vector<数据类型>,greater<数据类型> > 名; //小到大排序

若数据类型为结构体则需要定义结构体如下:

struct 结构体名 
{
	int x,y;
	bool operator < (const 结构体名 &a) const
	{
		return x<a.x;//按x的值降序序排列 
	}
};

2.10.2.常用操作

#include<bits/stdc++.h>
using namespace std;
int main() {
	priority_queue<int> a;  //默认大到小
	priority_queue<int, vector<int>, greater<int> > b;  //小到大
	a.push(3);//a:3 队首 3 队尾 3;
	a.push(2);//a:3 2 队首 3 队尾 2
	a.push(1);//a:3 2 1 队首 3 队尾 1
	a.push(9);//a:9 3 2 1 队首 9 队尾 1
	b.push(3);//b:3 队首 3 队尾 3
	b.push(2);//b:2 3 队首 2 队尾 3
	b.push(1);//b:1 2 3 队首 1 队尾 3
	b.push(9);//b:1 2 3 9 队首 3 队尾 9
	while (!a.empty()) {
		cout << a.top() << " ";
		a.pop();
	}
	cout << endl;
	while (!b.empty()) {
		cout << b.top() << " ";
		b.pop();
	}
	return 0;
}
/*运行结果:
9 3 2 1
1 2 3 9*/

3.string类

3.1.初始化

string对象的初始化和普通类型变量的初始化基本相同,只是string作为类,还有类的一些特性:使用构造函数初始化。如下表,第2 4 6条是作为类才有的初始化方式:

#include<bits/stdc++.h>
using namespace std;
int main() {
    string s1 = "";//初始化一个空串,将此串转化为string类
    string s1;//默认初始化,s1是一个空串,等价于string s1 = "",将此串转化为string类
    string s2 = "12345";//初始化一个串"12345",将此串转化为string类
    string s2("12345");//等价于string s2 = "12345",将此串转化为string类
    string s3 = s2;//将s2传给s3,将此串转化为string类
    string s3(s2);//等价于string s3 = s2,将此串转化为string类
    string s4 = string("value");//等价于string s4("value"),将此串转化为string类
    return 0;
}

3.2.输入输出

3.2.1.用cin输入

和int、double等类型的cin一样使用。需要说明一点:string对象会自动忽略开头的空白(既空格、换行符、制表符等),并从第一个真正的字符开始读入,直到遇到下一处空白(既空格、换行符、制表符等),即不能读入空格,换行符等

string s1;
cin >> s1;
cout << s1 << endl;

3.2.2.用getline读取一整行

getline的函数格式:getline(cin,string对象)
getline的作用是读取一整行,直到遇到换行符才停止读取,期间能读取像空格、Tab等的空白符。实例如下:

string s1;
getline(cin, s1);
cout << s1 << endl;

3.3.比较string的大小

string str = "HelloWorld";
string str1 = "HelloWorld ";

这种情况,尽管两者的前面对应的字符都一样,但是str1(多一个空格),所以str1>str。

string str = "HelloWorld";
string str1 = "HelloWi ";

这种情况比较的是第一个相异字符,根据字符值比较大小,因为i的字符值<o的字符值,所以str1< str。

string str = "HelloWorld";
string str1 = "HelloWorld";

这种情况,两个字符串完全一样,则str==str1。

3.4.字符串的加法

3.4.1.两个string对象相加

#include<bits/stdc++.h>
using namespace std;
int main() {
    string s1, s2;
    s1 = "abc";
    s2 = "def";
    s1 += s2;
    string s3;
    s3 = "abc" + s2 + "gh";
    cout << s3 << endl;
    cout << s1 << endl;
    return 0;
}
/*运行结果:
abcdefgh
abcdef*/

3.4.2.string对象加上一个字符(或字符串)字面值

3.4.2.1.例子
 
#include<bits/stdc++.h>
using namespace std;
int main() {
    string str = "Hello";
    string s2 = str + ',';
    s2 += ',';//等价于s2 = s2 + ','
    str += "abc";//等价于str = str + "abc"
    cout << s2 << endl << str << endl;
    return 0;
}
/*运行结果:
Hello,,
Helloabc*/
3.4.2.2.易错点
string str = "Hello";

(1)string s2 = str + "," + "world";
(2)string s3 = "Hello" + "," + str;

(1)正确;(2)错误

(2)错误的原因是:当string对象和字符或字符串字面值相加时,必须确保+号的两侧的运算对象至少有一个string类,这样才能返回一个类。"Hello" + "," 返回的不是string类,“Hello”是一个字符串,“,”是一个字符串,字符串之间不能加,string类之间可以加
至于(1),需要明白,str + “,”会返回一个string类。

3.5.获取和处理string中的每个字符

3.5.1.类似于字符数组,使用下标访问[]

获取字符串长度:size()

#include<bits/stdc++.h>
using namespace std;
int main() {
    string s = "Hello world!";
    cout << s[0] << endl;
    cout << s[s.size() - 1] << endl;
    cout << s << endl;
    s[0] = 'h';
    cout << s << endl;
    for (int i = 0; i < s.size(); i++) {
        cout << s[i] << ' ';
    }
}
/*运行结果:
H
!
Hello world!
hello world!
h e l l o   w o r l d !*/

3.5.2.使用迭代器访问

#include<bits/stdc++.h>
using namespace std;
int main() {
    string s = "Hello world!";
    for (auto i = s.begin(); i != s.end(); i++) {
        cout << *i << ",";
    }
    cout << endl;
}
/*运行结果:
H,e,l,l,o, ,w,o,r,l,d,!,*/

3.5.3.使用基于范围的for语句

基于范围的for语句是C++11新提供的一种语句,其语法形式是:
for(auto declaration:expression){
        statement;
}
declaration:定义一个变量,它每次的值都是expression中的基础元素
expression:一个已经定义的对象(变量)
statement:具体的语句

#include<bits/stdc++.h>
using namespace std;
int main() {
    string s = "Hello world!";
    for (auto i : s) {
        cout << i << ",";
    }
    cout << endl;
}
/*运行结果:
H,e,l,l,o, ,w,o,r,l,d,!,*/

3.6.string类型的相关方法

3.6.1.substr成员函数

substr(pos,n)//返回一个string对象,返回的对象包含s从pos下标开始的n个字符。pos和n均为可选参数。pos默认为下标0;n默认为s.size()-pos,pos需满足pos<=s.size().

string s ("value");
string s2 = s.substr();//s2为"value",大小为5
string s3 = s.substr(2);//s3为"lue",大小为3
string s4 = s.substr(5);//s3为"",大小为0
string s5 = s.substr(6);//错误,6大于s.size()
string s6 = s.substr(1,2);//s6为"al",大小为2
string s7 = s.substr(1,7);//s7为"alue",大小为4
string s8 = s.substr(5,7);//s8为"",大小为0
string s9 = s.substr(6,7);//错误,6大于s.size()

3.6.2.string对象的insert()

3.6.2.1.迭代器

1.iterator insert( iterator pos, CharT ch )
2.void insert( iterator pos, size_type count, CharT ch )
3.void insert( iterator pos, InputIt first, InputIt last )
4.插入初始化列表

string s1("value");
s1.insert(s1.begin(), 's');//执行后,s1为"svalue"
s1.insert(s1.begin(), 1, 's');//执行后,s1为"ssvalue"
s1.insert(s1.begin(), s1.begin(), ++s1.begin());//执行后,s1为"sssvalue"
s1.insert(s1.end(), {'1','2'});//执行后,s1为"sssvalue12"
3.6.2.2.下标
3.6.2.2.1. insert(Index,count,ch)

insert( size_type index, size_type count, CharT ch )//在下标index前插入count个字符ch。

string s1("value");
s1.insert(0,2,’s’); //执行后,s1为” ssvalue”
s1.insert(5,2,’s’); //执行后,s1为” valuess”
3.6.2.2.2.insert(index,s)

insert( size_type index, const basic_string& str )//在下标index前插入一个常量字符串或者string对象。

string s1("value");
string s3 = "value";
const char* cp = "value";
s1.insert(0,s3);//执行完后,s1为" valuevalue"
s1.insert(0,cp); //执行完后,s1为" valuevalue"
3.6.2.2.3.insert(index,str,index_str,count)

insert( size_type index, const basic_string& str, size_type index_str, size_type count );//在下标index前插入str中的从str[index_str]开始的count个字符。

string s1("value");
string s3 = "value";
s1.insert(0,s3,1,2);//执行后,s1为” alvalue”
3.6.2.2.4.insert(index,s,count)

insert( size_type index, const CharT* s, size_type count );//在index前插入常量字符串的count个字符。

string s1("value");
const char* cp = "value";
s1.insert(0, cp,3); //执行后,s1为” valvalue”

3.6.3.string对象的erase()

1.basic_string & erase(size_type pos, size_type n);//如果string对象s调用,它删除s从pos下标开始的n个字符,并返回删除后的s。当pos > s.size()时,报错。
2.iterator erase(const_iterator position);//如果string对象s调用,它删除s迭代器position位置的字符,并返回下一个字符的迭代器。
3.iterator erase(const_iterator first, const_iterator last);//如果string对象s调用,它删除s迭代器[first,last)区间的字符,并返回last字符的迭代器。

string s1("value");
string s2("value");
string s3("value");
string s4("value");
s1.erase();//执行后,s1为空
s2.erase(0,2); //执行后,s2为”lue”
s3.erase(s3.begin());//执行后,s3为”alue”
s4.erase(s4.begin(),++s4.begin());//执行后,s4为”alue”

3.6.4.字符串和整形间的转换

stoi(s)//将字符串s转换成整形
to_string(x)//将整形x转换成字符串

#include<bits/stdc++.h>
using namespace std;
int main() {
    string s = "123456";
    int x = stoi(s);
    int y = 654321;
    string str = to_string(y);
    cout << x << endl << str << endl;
    return 0;
}
/*运行结果:
123456
654321*/

3.6.5.搜索函数find()

str1.find(str2);/返回位置起始若失败则返回-1;

#include<bits/stdc++.h>
using namespace std;
int main() {
	string str = "There are two needles in this haystack with needles.";
	string str2 = "needle";
	int found = str.find(str2);//返回第一个"needles"n的下标
	cout << found << endl;
	string str3 = "123";
	found= str.find(str3);//返回第一个"123"的下表.查找失败
	cout << found << endl;
    return 0;
}
/*运行结果:
14
-1*/

4.STL库常用算法

4.1.快速排序std::sort

1.若对普通数组使用则为sort(array+l,array+I+r,(可选)cmp);代表将数组u.r]区间进行排序,若包含cmp参数则按照cmp规定排序方法,否则默认升序;
2若对STL顺序容器使用则为sort(vec.begin()+l,vec.begin()+l+r,cmp);代表将容器[.r]区间进行排序,若区间刚好为[0,size)则可以使用sort(vec.begin(),vec.end(),cmp);
3 std::sort中提供std:less()与std::greater()分别代表升序和降序。

4.2.二分查找

1.lower_bound与jupper_bound;前者查询第一个大于等于某个数的位置,后者查询第一个大于某个数的位置;
2.对于数组,则为lower_bound(a+l,a+l+r,val);返回指针;
对于顺序容器则为lower_bound(a.begin()+l,a.end()+I+r,val。返回迭代器。与sort相同,如果覆盖区间刚好为[o,size)则可以用begin和lend替代;
3若想获得下标,则对于数组减去数组名,对于迭代器减去begin()即可;
4关联容器中, map和set均重载在了自身的函数中。

4.3.交换std::swap()

注:STL容器中也重载了交换,由于vector的clear并不是真正的清除,可以用swap实现
如vector<int>().swap(a);并且交换两个vector的时间复杂度为o(1);交换静态数组为o(n),std:.array同样也是o(n);

4.4.std::fill()

由于memset(只能赋值按位相同的值,局限性很大。fill(begin,end,val);可以将[begin,end]全部设为val。

5.基于范围的for循环(C++11起)

5.1.理解

auto类型说明符用于自动推导变量等的类型。

例如:auto a = 1;自动推导为int
auto b = a+0.1;自动推导为double

5.2.语法

语法为for (auto 范围变量声明:范围表达式){
        循环语句;
}

5.3.使用

#include<bits/stdc++.h>
using namespace std;
int main() {
	int n; 
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	map<char, int> mp;
	mp['0'] = 1;
	mp['1'] = 2;
	set<int> st;
	st.insert(1);
	st.insert(5);
	for (auto x : v) {//推导为int
		cout << x << ' ';
	}
	cout << endl;
	cout << endl;
	for (auto x : mp) {//推导为迭代器
		cout << x.second << " " << x.first << endl;
	}
	cout << endl;
	for (auto x : st) {//推导为迭代器
		cout << x << " ";
	}
	return 0;
}
/*运行结果:
1 2

1 0
2 1

1 5*/

6.复杂度

6.1.时间复杂度

时间复杂度是衡量算法执行效率的重要指标,它描述了算法运行时间随输入规模增长的趋势。通常用大O符号(O)来表示时间复杂度。

在分析算法的时间复杂度时,我们通常关注算法执行所需要的基本操作次数(比如比较次数、赋值次数等),并用输入规模n的函数来表示这些基本操作次数的增长趋势。

常见的时间复杂度包括:

  1. O(1): 常数时间复杂度,表示算法的执行时间是一个常数,与输入规模无关。
  2. O(log n): 对数时间复杂度,通常是以2为底的对数,常见于二分查找等算法。
  3. O(n): 线性时间复杂度,算法执行时间与输入规模成线性关系。
  4. O(n log n): 线性对数时间复杂度,常见于快速排序、归并排序等分治算法。
  5. O(n^2): 平方时间复杂度,常见于简单的嵌套循环算法。
  6. O(2^n): 指数时间复杂度,通常出现在暴力穷举算法中,效率极低。

在实际分析算法的时间复杂度时,我们通常考虑最坏情况下的时间复杂度,因为它能够提供对算法性能的较为悲观但更为全面的估计。

6.2.空间复杂度

空间复杂度是另一个重要的算法性能指标,描述了算法在执行过程中所需的额外空间或内存量。与时间复杂度类似,空间复杂度也使用大O符号(O)来表示。

在分析算法的空间复杂度时,我们通常关注算法在运行过程中所需要的额外空间,不包括输入数据所占用的空间。空间复杂度包括以下几种情况:

  1. O(1): 常数空间复杂度,表示算法所需的额外空间是一个常数,与输入规模无关。
  2. O(n): 线性空间复杂度,表示算法所需的额外空间与输入规模成线性关系。
  3. O(n^2): 平方空间复杂度,表示算法所需的额外空间与输入规模的平方成正比。
  4. O(log n): 对数空间复杂度,通常是以2为底的对数,与输入规模的对数成正比。

在实际应用中,我们通常更关注算法的时间复杂度,因为空间通常比时间更为宝贵。但是,在一些内存受限的环境下(比如嵌入式系统、移动设备等),空间复杂度也是需要考虑的重要因素。

在分析空间复杂度时,我们通常考虑算法所使用的额外空间的最大值,即算法在任何时刻所占用的最大空间量。

  • 24
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lin..6

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值