ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

C++ 语言基础

2026/8/6 19:09:41 拓冰建站 浏览量
C++ 语言基础

学习目标:C++ 语言基础

学习内容:基础语法

本文针对初学者,介绍 C++ 的基本使用,包括控制语句、标准库的常用数据结构等,以便快速上手刷题。

例如:

  1. 标准输出
  2. 控制语句
  3. 基本数据结构
  4. 总结

标准输出:

C++ 的标准输出是 cout,用 << 运算符把需要打印的内容传递给 cout,endl 是换行符。

inta=10;// 输出:10cout<<a<<endl;// 可以串联输出// 输出:Hello, World!cout<<"Hello"<<", "<<"World!"<<endl;string s="abc";// 输出:abc 10cout<<s<<" "<<a<<endl;

当然,C 语言的 printf 函数也可以用,但是 cout 更加方便,所以我们一般用 cout。

标准输出:

编程语言的控制语句一般都比较简单,最常见的无非就是条件判断和循环,下面简单介绍一下。

条件判断 if else

inta=10;if(a>5){cout<<"a > 5"<<endl;}elseif(a==5){cout<<"a == 5"<<endl;}else{cout<<"a < 5"<<endl;}// 输出:a > 5

循环 for/while

for 和 while 都可以用来做循环,for 循环一般用于已知循环次数的情况,while 循环一般用于未知循环次数的情况。

// 0 1 2 3 4for(inti=0;i<5;i++){cout<<i<<" ";}intnum=100;// 100 50 25 12 6 3 1while(num>0){cout<<num<<" ";num/=2;}

基本数据结构

对于标准库的常用数据结构。你在写算法题时可以通过直接使用 vector、set、map 等关键字创建数据结构,当然你也要导入相应的头文件。

动态数组 vector

vector 是 C++ 标准库的动态数组。

大家以前学 C 语言的时候肯定学过 malloc, int[n] 等方式来创建静态数组,但是这种方式非常麻烦,而且容易出错。我们做算法题的时候一般使用动态数组 vector,而且题目给的输入一般也是 vector 类型。

vector 的初始化方法如下:
#include<vector>intn=7,m=8;// 初始化一个 int 型的空数组 numsvector<int>nums;// 初始化一个大小为 n 的数组 nums,数组中的值默认都为 0vector<int>nums(n);// 初始化一个元素为 1, 3, 5 的数组 numsvector<int>nums{1,3,5};// 初始化一个大小为 n 的数组 nums,其值全都为 2vector<int>nums(n,2);// 初始化一个二维 int 数组 dpvector<vector<int>>dp;// 初始化一个大小为 m * n 的布尔数组 dp,// 其中的值都初始化为 truevector<vector<bool>>dp(m,vector<bool>(n,true));
vector 的常用操作:
#include<iostream>#include<vector>usingnamespacestd;intmain(){intn=10;// 数组大小为 10,元素值都为 0vector<int>nums(n);// 输出 0 (false)cout<<nums.empty()<<endl;// 输出:10cout<<nums.size()<<endl;// 在数组尾部插入一个元素 20nums.push_back(20);// 输出:11cout<<nums.size()<<endl;// 得到数组最后一个元素的引用// 输出:20cout<<nums.back()<<endl;// 删除数组的最后一个元素(无返回值)nums.pop_back();// 输出:10cout<<nums.size()<<endl;// 可以通过方括号直接取值或修改nums[0]=11;// 输出:11cout<<nums[0]<<endl;// 在索引 3 处插入一个元素 99nums.insert(nums.begin()+3,99);// 删除索引 2 处的元素nums.erase(nums.begin()+2);// 交换 nums[0] 和 nums[1]swap(nums[0],nums[1]);// 遍历数组// 0 11 99 0 0 0 0 0 0 0for(inti=0;i<nums.size();i++){cout<<nums[i]<<" ";}cout<<endl;}

以上就是 C++ vector 在文章中的常用方法,无非就是用索引取元素以及 push_back, pop_back 方法,就刷算法题而言,这些就够了。
因为根据「数组」的特性,利用索引访问元素很高效,从尾部增删元素也是很高效的;而从中间或头部增删元素要涉及搬移数据,很低效。

双链表 list:

list 是 C++ 标准库中的双向链表容器。
初始化方法:

#include<list>usingnamespacestd;intn=7;// 初始化一个空的双向链表 lstlist<int>lst;// 初始化一个大小为 n 的链表 lst,链表中的值默认都为 0list<int>lst(n);// 初始化一个包含元素 1, 3, 5 的链表 lstlist<int>lst{1,3,5};// 初始化一个大小为 n 的链表 lst,其中值都为 2list<int>lst(n,2);
list 的常用方法:
#include<iostream>#include<list>usingnamespacestd;intmain(){// 初始化链表list<int>lst{1,2,3,4,5};// 检查链表是否为空,输出:falsecout<<lst.empty()<<endl;// 获取链表的大小,输出:5cout<<lst.size()<<endl;// 在链表头部插入元素 0lst.push_front(0);// 在链表尾部插入元素 6lst.push_back(6);// 获取链表头部和尾部元素,输出:0 6cout<<lst.front()<<" "<<lst.back()<<endl;// 删除链表头部元素lst.pop_front();// 删除链表尾部元素lst.pop_back();// 在链表中插入元素autoit=lst.begin();// 移动到第三个位置advance(it,2);// 在第三个位置插入 99lst.insert(it,99);// 删除链表中某个元素it=lst.begin();// 移动到第二个位置advance(it,1);// 删除第二个位置的元素lst.erase(it);// 遍历链表// 输出:1 99 3 4 5for(intval:lst){cout<<val<<" ";}cout<<endl;return0;}

一般来说,当我们想在头部增删元素时会使用双链表,因为它在头部增删元素的效率比 vector 高。
但我们通过索引访问元素,这种场景下我们会使用 vector。

队列 queue

queue 是 C++ 标准库中的队列容器,基于先进先出(FIFO)的原则。队列适用于只允许从一端(队尾)添加元素、从另一端(队头)移除元素的场景。

#include<iostream>#include<queue>usingnamespacestd;intmain(){// 初始化一个空的整型队列 qqueue<int>q;// 在队尾添加元素q.push(10);q.push(20);q.push(30);// 检查队列是否为空,输出:falsecout<<q.empty()<<endl;// 获取队列的大小,输出:3cout<<q.size()<<endl;// 获取队列的队头和队尾元素,输出:10 和 30cout<<q.front()<<" "<<q.back()<<endl;// 删除队头元素q.pop();// 输出新的队头元素:20cout<<q.front()<<endl;return0;}

栈 stack

栈是一种后进先出(LIFO)的数据结构,栈适用于只允许在一端(栈顶)添加或移除元素的场景。

stack 是 C++ 标准库中的栈容器,关于栈的实现原理和使用场景。

#include<iostream>#include<stack>usingnamespacestd;intmain(){// 初始化一个空的整型栈 sstack<int>s;// 向栈顶添加元素s.push(10);s.push(20);s.push(30);// 检查栈是否为空,输出:falsecout<<s.empty()<<endl;// 获取栈的大小,输出:3cout<<s.size()<<endl;// 获取栈顶元素,输出:30cout<<s.top()<<endl;// 删除栈顶元素s.pop();// 输出新的栈顶元素:20cout<<s.top()<<endl;return0;}

哈希表 unordered_map

unordered_map 是 C++ 标准库中的一种哈希表实现,它提供了基于键值对(key-value)的存储,提供了常数时间复杂度的查找、插入和删除键值对的操作。

#include<unordered_map>usingnamespacestd;// 初始化一个空的哈希表 mapunordered_map<int,string>hashmap;// 初始化一个包含一些键值对的哈希表 mapunordered_map<int,string>hashmap{{1,"one"},{2,"two"},{3,"three"}};
unordered_map 的常用方法如下:

特别注意:访问不存在的键会自动插入键值对
在 C++ 的哈希表中,如果你访问一个不存在的键,它会自动创建这个键,对应的值是默认构造的值。
这一点和其他语言不同,需要格外注意。记住访问值之前要先判断键是否存在,否则可能会意外地创建新键,导致算法出错。详见下面的示例。

#include<iostream>#include<unordered_map>usingnamespacestd;intmain(){// 初始化哈希表unordered_map<int,string>hashmap{{1,"one"},{2,"two"},{3,"three"}};// 检查哈希表是否为空,输出:0 (false)cout<<hashmap.empty()<<endl;// 获取哈希表的大小,输出:3cout<<hashmap.size()<<endl;// 查找指定键是否存在// 注意 contains 方法是 C++20 新增的// 输出:Key 2 -> twoif(hashmap.contains(2)){cout<<"Key 2 -> "<<hashmap[2]<<endl;}else{cout<<"Key 2 not found."<<endl;}// 获取指定键对应的值,若不存在会返回默认构造的值// 输出空字符串cout<<hashmap[4]<<endl;// 插入一个新的键值对hashmap[4]="four";// 获取新插入的值,输出:fourcout<<hashmap[4]<<endl;// 删除键值对hashmap.erase(3);// 检查删除后键 3 是否存在// 输出:Key 3 not found.if(hashmap.contains(3)){cout<<"Key 3 -> "<<hashmap[3]<<endl;}else{cout<<"Key 3 not found."<<endl;}// 遍历哈希表// 输出(顺序可能不同):// 4 -> four// 2 -> two// 1 -> onefor(constauto&pair:hashmap){cout<<pair.first<<" -> "<<pair.second<<endl;}// 特别注意,访问不存在的键会自动创建这个键unordered_map<int,string>hashmap2;// 键值对的数量是 0cout<<hashmap2.size()<<endl;// 0// 访问不存在的键,会自动创建这个键,对应的值是默认构造的值cout<<hashmap2[1]<<endl;// empty stringcout<<hashmap2[2]<<endl;// empty string// 现在键值对的数量是 2cout<<hashmap2.size()<<endl;// 2return0;}
哈希集合 unordered_set

unordered_set 是 C++ 标准库中的一种哈希集合实现,用于存储不重复的元素,常见使用场景是对元素进行去重。

#include<iostream>#include<unordered_set>usingnamespacestd;intmain(){// 初始化哈希集合unordered_set<int>hashset{1,2,3,4};// 检查哈希集合是否为空,输出:0 (false)cout<<hashset.empty()<<endl;// 获取哈希集合的大小,输出:4cout<<hashset.size()<<endl;// 查找指定元素是否存在// 输出:Element 3 found.if(hashset.contains(3)){cout<<"Element 3 found."<<endl;}else{cout<<"Element 3 not found."<<endl;}// 插入一个新的元素hashset.insert(5);// 删除一个元素hashset.erase(2);// 输出:Element 2 not found.if(hashset.contains(2)){cout<<"Element 2 found."<<endl;}else{cout<<"Element 2 not found."<<endl;}// 遍历哈希集合// 输出(顺序可能不同):// 1// 3// 4// 5for(constauto&element:hashset){cout<<element<<endl;}return0;}

传值和传引用

在 C++ 中,函数参数的传递方式主要有两种:
传值和传引用。理解它们的区别对于编写高效的算法代码至关重要,特别是在处理大量数据或需要修改原始数据时。

传值(Pass by Value)

传值是指将函数参数的一个副本传递给函数,在函数内部对该副本的修改不会影响到原始数据。
下面是一个例子:

#include<iostream>usingnamespacestd;voidmodifyValue(intx){x=10;// 只修改副本,不会影响原始数据}intmain(){intnum=5;modifyValue(num);// 输出:5cout<<"After modifyValue, num = "<<num<<endl;return0;}

在上述代码中,num 的值在调用 modifyValue 后并未改变,因为传入的是 num 的副本,函数内的修改仅影响副本。

传引用(Pass by Reference)

传引用是指将实参的地址传递给函数,函数可以直接操作原始数据。这意味着对参数的修改会直接影响原始数据。
下面是一个例子:

#include<iostream>usingnamespacestd;voidmodifyReference(int&x){x=10;// 修改原始数据}intmain(){intnum=5;modifyReference(num);// 输出:10cout<<"After modifyReference, num = "<<num<<endl;return0;}

在上述代码中,num 的值被修改为 10,因为我们传递的是 num 的引用,函数内对 x 的修改直接影响了 num。

做算法题时的选择

根据经验,如果是传递基本类型,比如 int、bool 等,用传值比较多,因为这类数据一般不需要在函数内部修改,而且复制的开销很小。

如果是传递容器数据结构,比如 vector、unordered_map 等,用传引用比较多,因为可以避免复制数据副本的开销,而且容器一般需要在函数内部修改。

特别注意一个可能出现的问题,就是当递归函数的参数中有容器数据结构时,千万别使用传值的方式,否则每次递归都会创建一个数据副本,消耗大量的内存和时间,非常容易导致超时或者超内存的错误。

总结

这些基础语法和数据结构的用法可以帮助我们完成一些基础的算法练习,那么有感兴趣的朋友可以私信一起学习。多多关注!!这是我更新的动力!!!!