C++基本操作

193 阅读19分钟

输入输出

C/C++标准输入输出终极最全解析(不全捶我)- scanf、fgets、getchar、cin,printf、fputs、putchar、cout等_c++輸入輸出該用什麼_MidoQ的博客-CSDN博客

cin

cin是 C++ 的标准输入流对象,即istream类的一个对象实例。

cin有自己的缓冲区,但默认情况下是与stdin同步的,因此在 C++ 中可以混用 C++ 和 C 风格的输入输出(在不手动取消同步的情况下)。cinstdin一样是行缓冲,即遇到换行符时才会将数据同步到输入缓冲区。

  • 对空白字符的处理

cin对空白字符的处理与scanf一致。即:跳过开头空白字符,遇到空白字符停止读取,且空白字符(包括换行符)残留在缓冲区。

// 待读入的两个字符串,其中有换行符
adfadfsgdas
dafasdgaa
    
// 如果在笔试编程中无法读到换行符并中止
while(cin >> c)
// 这样就可以分别读串……
string s;
cin >> s;

cin.get()

读取单个或指定长度的字符,包括空白字符。

char a, b;
char str[20];
 
// 读取一个字符,读取失败时返回0,多余字符残留在缓冲区(包括换行符)
a = cin.get();
 
// 读取一个字符,读取失败时返回EOF,多余字符残留在缓冲区(包括换行符)
cin.get(b);
 
// 在遇到指定终止字符(参数3)前,至多读取n-1个(参数2)字符
// 当不指定终止字符时,默认为换行符\n
// 如果输入的字符个数小于等于n-1(不含终止字符),则终止字符不残留在缓冲区
// 如果输入的字符个数多于n-1(不含终止字符),则余下字符将残留在缓冲区
cin.get(str, sizeof(str), '\n');

printf

printf怎么输出? 如何正确使用%s,%c,%d,%p?=>这篇详解用法_printf("%s")_踏过山河,踏过海的博客-CSDN博客

  • %c
#include<stdio.h>

int main(void) {
    char op[20] = { "123" };
    char*p = op;
    printf("%c", *op);//=>1 =>*op是值
    return 0;
}
  • %s
#include<stdio.h>

int main(void) {
    char op[20] = { “123” };
    char*p = op;
    //op是指针(地址),*op是值,p是指针,*p是值
    printf("%s", op);//=>123 =>op是指针(地址)或者printf("%s",p);=>p是指针
    return 0;
}

判断是否为字母或数字

  • 范围确定

判断一个字符是否为:

小写字母:字符大于等于a,小于等于z;

大写字母:字符大于等于A,小于等于Z;

数字:字符大于等于0,小于等于9;

  • STL库函数判断

字母(不区分大小写):isalpha();

大写字母:isupper();

小写字母:islower();

数字:isdigit();

字母和数字:isalnum();

  • 大小写字母转化:

    (1)转化为大写:toupper();

    (2)转化为小写:tolower();

-----数据结构

和队列基本操作相同:

  • top 访问队头元素
  • empty 队列是否为空
  • size 返回队列内元素个数
  • push 插入元素到队尾 (并排序)
  • emplace 原地构造一个元素并插入队列
  • pop 弹出队头元素
  • swap 交换内容
#include <queue>
priority_queue<Type, Container, Functional>
//Type 就是数据类型;
//Container 就是容器类型(Container必须是用数组实现的容器,比如vector,deque等等,但不能用 list。STL里面默认用的是vector);
//Functional 就是比较的方式,当需要用自定义的数据类型时才需要传入这三个参数,使用基本数据类型时,只需要传入数据类型,默认是大顶堆.
    
    
//升序队列
priority_queue <int,vector<int>,greater<int> > q;
//降序队列
priority_queue <int,vector<int>,less<int> >q;

//greater和less是std实现的两个仿函数(就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了)
//对于基础类型 默认是大顶堆
priority_queue<int> a; 
//等同于 priority_queue<int, vector<int>, less<int> > a;


priority_queue<int, vector<int>, greater<int> > c;  //这样就是小顶堆
priority_queue<string> b;

for (int i = 0; i < 5; i++) 
{
    a.push(i);	// 4 3 2 1 0
    c.push(i);	// 0 1 2 3 4
}

b.push("abc");
b.push("abcd");
b.push("cbd");	// cbd abcd abc

插入

C++ STL vector插入元素(insert()和emplace())详解 (biancheng.net)

  • insert
语法格式用法说明
iterator insert(pos,elem)在迭代器 pos 指定的位置之前插入一个新元素elem,并返回表示新插入元素位置的迭代器。
iterator insert(pos,n,elem)在迭代器 pos 指定的位置之前插入 n 个元素 elem,并返回表示第一个新插入元素位置的迭代器。
iterator insert(pos,first,last)在迭代器 pos 指定的位置之前,插入其他容器(不仅限于vector)中位于 [first,last) 区域的所有元素,并返回表示第一个新插入元素位置的迭代器。
iterator insert(pos,initlist)在迭代器 pos 指定的位置之前,插入初始化列表(用大括号{}括起来的多个元素,中间有逗号隔开)中所有的元素,并返回表示第一个新插入元素位置的迭代器。
#include <iostream> 
#include <vector> 
#include <array> 
using namespace std;

int main()
{
    std::vector<int> demo{1,2};
    //第一种格式用法
    demo.insert(demo.begin() + 1, 3);//{1,3,2}

    //第二种格式用法
    demo.insert(demo.end(), 2, 5);//{1,3,2,5,5}

    //第三种格式用法
    std::array<int,3>test{ 7,8,9 };
    demo.insert(demo.end(), test.begin(), test.end());//{1,3,2,5,5,7,8,9}

    //第四种格式用法
    demo.insert(demo.end(), { 10,11 });//{1,3,2,5,5,7,8,9,10,11}

    for (int i = 0; i < demo.size(); i++) {
        cout << demo[i] << " ";
    }
    return 0;
}
  • emplace:是 C++ 11 标准新增加的成员函数,用于在 vector 容器指定位置之前插入一个新的元素。
iterator emplace (const_iterator pos, args...);
//其中,pos 为指定插入位置的迭代器;args... 表示与 /* 新插入元素的构造函数 * /相对应的多个参数;该函数会返回表示新插入元素位置的迭代器。
std::vector<int> demo1{1,2};
//emplace() 每次只能插入一个 int 类型元素
demo1.emplace(demo1.begin(), 3);	//3 1 2
  • emplace 比 insert 的效率更高。 emplace() 在插入元素时,是在容器的指定位置直接构造元素,而不是先单独生成,再将其复制(或移动)到容器中。因此,在实际使用中,推荐大家优先使用 emplace()。

vector、map、list

C++最经典的三大容器Vector、Map、List_c++ vector map_你不懂细节的博客-CSDN博客

size()的雷点

size()方法返回的是unsigned int,假如返回了2, 1 - size()=2 < 0 是false的,因为左边会强制转化为unsigned int,因此就不会为负数。

解决办法: 1.左边转换成int; 2.把size移到另一边。

vector

地址不变,内存空间连续,所以在中间进行插入和删除时会造成内存块拷贝,如果插入的是结构体或者类,则会造成构造和析构,性能不是特别高,对结尾元素操作最快。

image.png

优点:支持随机访问,即下标访问和迭代器访问,所以查询效率高

缺点:往头部或中部插入或删除元素时,为了保持原本的相对次序,插入或删除之后的所有元素都必须移动,所以插入效率比较低

适用场景:适用于对象简单,变化较小,并且频繁随机访问的场景。

list

双向链表实现而成。

元素存放与堆区,每个元素都是放在一块内存中,他的内存空间可以是不连续的,通过指针来进行数据的访问。

没有提供迭代器,每删除一个元素都会释放它占用的内存,可以在任意地方插入和删除,访问头尾两个元素最快,其他元素的访问时间一样。

image.png 优点:内存不连续,动态操作,可以在任意位置插入或删除且效率高。

缺点:不支持随机访问。

适用场景:经常进行插入和删除并且不经常随机访问的场景。

map

详情看下面部分。

关联容器

和顺序容器不同,是关联容器。关联容器中的元素是按关键字来保存和访问的。与之相对,顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的。

关联容器包括set 和 map。

unordered_map

(49条消息) 一文看懂哈希表并学会使用C++ STL 中的哈希表_哈希表end函数_嗯行家啊的博客-CSDN博客

#include<unordered_map>
  • 声明
unordered_map<elemType_1, elemType_2> var_name; //声明一个没有任何元素的哈希表,
//其中elemType_1和elemType_2是模板允许定义的类型,如要定义一个键值对都为Int的哈希表:

// pair<,>不行,要自定义函数
unordered_map<int, int> map;
  • 初始化
unordered_map<int, int> hmap{ {1,10},{2,12},{3,13} };
//如果知道要创建的哈希表的元素个数时,也可以在初始化列表中指定元素个数
unordered_map<int, int> hmap{ {{1,10},{2,12},{3,13}},3 };



//当我们想向哈希表中添加元素时也可以直接通过下标运算符添加元素,格式为: mapName[key]=value;
//如:hmap[4] = 14;
//但是这样的添加元素的方式会产生覆盖的问题,也就是当hmap中key为4的存储位置有值时,
//再用hmap[4]=value添加元素,会将原哈希表中key为4存储的元素覆盖
hmap[4] = 14;
hmap[5] = 15;
cout << hmap[4];  //结果为15



//通过insert()函数来添加元素的结果和通过下标来添加元素的结果一样,不同的是insert()可以避免覆盖问题,
//insert()函数在同一个key中插入两次,第二次插入会失败
hmap.insert({ 5,15 });
hmap.insert({ 5,16 });
cout << hmap[5];  //结果为15



unordered_map<int, int> hmap{ {1,10},{2,12},{3,13} };
unordered_map<int, int> hmap1(hmap);
  • 常用函数
// erase()
hmap.erase(iter_begin);  //删除开始位置的元素
hmap.erase(iter_begin, iter_end); //删除开始位置和结束位置之间的元素
hmap.erase(3); //删除key==3的键值对


// find()
// 以key作为参数寻找哈希表中的元素,如果哈希表中存在该key值则返回该位置上的迭代器,否则返回哈希表最后一个元素下一位置上的迭代器
unordered_map<int, int> hmap{ {1,10},{2,12},{3,13} };
unordered_map<int, int>::iterator iter;
iter = hmap.find(2); //返回key==2的迭代器,可以通过iter->second访问该key对应的元素
if(iter != hmap.end())  cout << iter->second;


// at()
// 根据key查找哈希表中的元素
unordered_map<int, int> hmap{ {1,10},{2,12},{3,13} };
int elem = hmap.at(3);

  • 遍历哈希表
// 值传递遍历
for(pair<int,int> kv:map){
    cout<<kv.first<<kv.second<<endl;
}
for(auto kv:map){	// auto
    cout<<kv.first<<kv.second<<endl;
}

// 引用传递遍历
for(const pair<int,int>& kv:map){	//&
    cout<<kv.first<<kv.second<<endl;
}
for(auto& kv:map){
    cout<<kv.first<<kv.second<<endl;
}

// 使用迭代器遍历
for(auto it=map.begin();it!=map.end();it++){
    cout<<it->first<<it->second<<endl;
}

unordered_set

C++ STL unordered_set容器完全攻略 (biancheng.net)

集合底层实现是否有序数值是否可以重复能否更改数值查询效率增删效率
std::set红黑树有序O(logn)O(logn)
std::multiset红黑树有序O(logn)O(logn)
std::unordered_set哈希表无序O(1)O(1)
映射底层实现是否有序数值是否可以重复能否更改数值查询效率增删效率
std::map红黑树key有序key不可重复key不可修改O(logn)O(logn)
std::multimap红黑树key有序key可重复key不可修改O(logn)O(logn)
std::unordered_map哈希表key无序key不可重复key不可修改O(1)O(1)
  • 特性

    • 不再以键值对的形式存储数据,而是直接存储数据的值;

    • 容器内部存储的各个元素的值都互不相等,且不能被修改;

    • 不会对内部存储的数据进行排序(这和该容器底层采用哈希表结构存储数据有关,可阅读《C++ STL无序容器底层实现原理》一文做详细了解);

    对于 unordered_set 容器不以键值对的形式存储数据,读者也可以这样认为,即 unordered_set 存储的都是键和值相等的键值对,为了节省存储空间,该类容器在实际存储时选择只存储每个键值对的值。

  • 头文件

#include <unordered_set>
  • 创建

通过调用 unordered_set 模板类的默认构造函数,可以创建空的 unordered_set 容器。比如:

unordered_set<string> uset;

创建的同时进行初始化:

unordered_set<string> uset{ "string1","string2","string3" };

利用拷贝构造函数:

unordered_set<string> uset2(uset);

直接用vector构造:

unordered_set<int> to_delete_set(to_delete.begin(), to_delete.end());
  • 添加新元素
uset.insert(x);
  • 成员方法
成员方法功能
begin()返回指向容器中第一个元素的正向迭代器。
end();返回指向容器中最后一个元素之后位置的正向迭代器。
cbegin()和 begin() 功能相同,只不过其返回的是 const 类型的正向迭代器。
cend()和 end() 功能相同,只不过其返回的是 const 类型的正向迭代器。
empty()若容器为空,则返回 true;否则 false。
size()返回当前容器中存有元素的个数。
max_size()返回容器所能容纳元素的最大个数,不同的操作系统,其返回值亦不相同。
find(key)查找以值为 key 的元素,如果找到,则返回一个指向该元素的正向迭代器;反之,则返回一个指向容器中最后一个元素之后位置的迭代器(如果 end() 方法返回的迭代器)。
count(key)在容器中查找值为 key 的元素的个数。
equal_range(key)返回一个 pair 对象,其包含 2 个迭代器,用于表明当前容器中值为 key 的元素所在的范围。
emplace()向容器中添加新元素,效率比 insert() 方法高。
emplace_hint()向容器中添加新元素,效率比 insert() 方法高。
insert()向容器中添加新元素。
erase()删除指定元素。
clear()清空容器,即删除容器中存储的所有元素。
swap()交换 2 个 unordered_set 容器存储的元素,前提是必须保证这 2 个容器的类型完全相等。
bucket_count()返回当前容器底层存储元素时,使用桶(一个线性链表代表一个桶)的数量。
max_bucket_count()返回当前系统中,unordered_set 容器底层最多可以使用多少桶。
bucket_size(n)返回第 n 个桶中存储元素的数量。
bucket(key)返回值为 key 的元素所在桶的编号。
load_factor()返回 unordered_set 容器中当前的负载因子。负载因子,指的是的当前容器中存储元素的数量(size())和使用桶数(bucket_count())的比值,即 load_factor() = size() / bucket_count()。
max_load_factor()返回或者设置当前 unordered_set 容器的负载因子。
rehash(n)将当前容器底层使用桶的数量设置为 n。
reserve()将存储桶的数量(也就是 bucket_count() 方法的返回值)设置为至少容纳 count 个元(不超过最大负载因子)所需的数量,并重新整理容器。
hash_function()返回当前容器使用的哈希函数对象。

set

(53条消息) C++中set用法详解_c++ set_Donny-You的博客-CSDN博客

  • 元素自动排序,这为查找元素提供了良好性能,但同时也造成了一个重要限制:不能直接改变元素值,因为这会打乱原本正确的顺序。(unordered_set 里面的元素是无序的)
  • 底部实现是非常高效的平衡检索二叉树:红黑树(Red-Black Tree)RB树。
#include <set>
begin()        ,返回set容器的第一个元素

end()      ,返回set容器的最后一个元素

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

empty()    ,判断set容器是否为空

max_size()   ,返回set容器可能包含的元素最大个数

size()      ,返回当前set容器中的元素个数

rbegin     ,返回的值和end()相同

rend()     ,返回的值和rbegin()相同

队列

priority_queue

C++ priority_queue(STL priority_queue)用法详解 (biancheng.net)

  • 最小堆:最小的元素会排在队列前面
priority_queue<string, vector<string>,greater<string>> words1 
  • 最大堆【默认】
priority_queue<string, vector<string>,less<string>> words1 
  • 基础操作
push(const T& obj)://将obj的副本放到容器的适当位置,这通常会包含一个排序操作。
push(T&& obj)://将obj放到容器的适当位置,这通常会包含一个排序操作。
emplace(T constructor a rgs...):// 通过调用传入参数的构造函数,在序列的适当位置构造一个T对象。为了维持优先顺序,通常需要一个排序操作。
top():// 返回优先级队列中第一个元素的引用。
pop():// 移除第一个元素。
size():// 返回队列中元素的个数。
empty():// 如果队列为空的话,返回true。
swap(priority_queue<T>& other):// 和参数的元素进行交换,所包含对象的类型必须相同。

字符串的操作

字符串和数字的转换

(49条消息) C++:字符串和数字的转换_number 转std::string_miaow~miaow的博客-CSDN博客

  • 数字转字符串
#include <string>

std::string to_string(int value);   
std::string to_string(long value);   
std::string to_string(long long value);
std::string to_string(unsigned value);
std::string to_string(unsigned long value);
std::string to_string(unsigned long long value);
std::string to_string(float value);
std::string to_string(double value);
std::string to_string(long double value);
  • 字符串转数字
int std::stoi(const std::string& str,std::size_t* pos=0,int base = 10);
int std::stoi(const std::wstring& str,std::size_t* pos=0,int base = 10);

long std::stol(const std::string& str,std::size_t* pos=0,int base = 10);
long std::stol(const std::wstring& str,std::size_t* pos=0,int base = 10);

float std::stof(const std::string& str,std::size_t* pos=0);
float std::stof(const std::wstring& str,std::size_t* pos=0);
double std::stod(const std::string& str,std::size_t* pos=0);
double std::stod(const std::wstring& str,std::size_t* pos=0);

增删改查(含vector)

C++ string详解,C++字符串详解 (biancheng.net)

(49条消息) vector 详解(C++)_vector c++_偏安一隅任逍遥的博客-CSDN博客

  • 提取字符串
string substr (size_t pos = 0, size_t len = npos) const;
string s1 = "first second third";
string s2;
s2 = s1.substr(6, 6);

image.png

// erase
vector<int>demo{ 1,2,3,4,5 };
auto iter = demo.erase(demo.begin() + 1);//删除元素 2
// remove
deque<double> samples{ 1.5, 2.6, 0.0, 3.1, 0.0, 0.0, 4.1, 0.0, 6.7, 0.0 };
samples.erase(remove(begin(samples), end(samples), 0.0), end(samples));

在vector中,remove的时候只是通过迭代器的指针向前移动来删除,将没有被删除的元素放在链表的前面,并返回一个指向新的超尾值的迭代器。由于remove()函数不是vector成员函数,因此不能调整vector容器的长度。(49条消息) C++ remove()函数用法详解(深入了解,一文学会)_双子座断点的博客-CSDN博客

-----算法

排序

sort

#include <algorithm>
using namespace std;

sort(first_pointer,first_pointer+n,cmp);
sort(v.begin(), v.end());
  • 原理

sort并不是简单的快速排序,它对快速排序进行了优化。此外,它还结合了插入排序堆排序

系统会根据数据形式和数据量自动选择合适的排序方法。它每次排序中不只选择一种方法,

比如给一个数据量较大的数组排序,开始采用快速排序,分段递归,

分段之后每一段的数据量达到一个较小值后它就不继续往下递归,而是选择插入排序,

如果递归的太深,他会选择堆排序。

  • sort扩展

【自定义比较函数】

int A[100];
bool cmp1(int a,int b)
{
	return a>b;		//降序排列
	//return a<b;	//默认的升序排列
}
sort(A,A+100,cmp1);

【使用标准库函数】

functional提供了一堆基于模板的比较函数对象:equal_to、not_equal_to、greater、greater_equal、less、less_equal。

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <functional>
using namespace std;

//简单使用方法
sort(A,A+100,greater<int>());//降序排列
sort(A,A+100,less<int>());//升序排列

【重载结构体或类的比较运算符】

//情况一:在结构体内部重载
typedef struct Student{
    int id;
    string name;
    double grade;

    bool operator<(const Student& s)
    {
        return id>s.id;		//降序排列
        //return id<s.id;	//升序排列
    }
};
vector<Student> V;
sort(V.begin(),V.end());

查找

find

底层实现:顺序查找(逐个遍历)。

lower_bound

底层实现:二分查找,数据处于有序状态。

用于在指定区域内查找不小于(≥)目标值的第一个元素。也就是说,使用该函数在指定范围内查找某个目标值时,最终查找到的不一定是和目标值相等的元素,还可能是比目标值大的元素。

C++ lower_bound()函数用法详解 (biancheng.net)

#include <algorithm>
//在 [first, last) 区域内查找不小于 val 的元素
ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last,
                             const T& val);
//在 [first, last) 区域内查找第一个不符合 comp 规则的元素
ForwardIterator lower_bound (ForwardIterator first, ForwardIterator last,
                             const T& val, Compare comp);

返回一个正向迭代器。查找成功时,迭代器指向找到的元素;反之,如果查找失败,迭代器的指向和 last 迭代器相同。

int a[5] = { 1,2,3,4,5 };
//从 a 数组中找到第一个不小于 3( >= 3)的元素
int *p = lower_bound(a, a + 5, 3);




class mycomp2 {
public:
    bool operator()(const int& i, const int& j) {
        return i>j;
    }
};

vector<int> myvector{ 4,5,3,1,2 };
//根据 mycomp2 规则,从 myvector 容器中找到第一个违背 mycomp2 规则的元素
vector<int>::iterator iter = lower_bound(myvector.begin(), myvector.end(),3,mycomp2());

upper_bound

在指定范围内查找**大于(>)**目标值的第一个元素.

#include <algorithm>
//查找[first, last)区域中第一个大于 val 的元素。
ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last,
                             const T& val);
//查找[first, last)区域中第一个不符合 comp 规则的元素
ForwardIterator upper_bound (ForwardIterator first, ForwardIterator last,
                             const T& val, Compare comp);

取整

C语言(C++)中:详解floor函数、ceil函数和round函数_c语言 floor 函数-CSDN博客

向上取整 ceil()

向下取整floor()

四舍五入round()

腾讯一面,跪..... (qq.com)

游戏开发面试真题合集 - 知乎 (zhihu.com)