学习目标C 语言基础学习内容基础语法本文针对初学者介绍 C 的基本使用包括控制语句、标准库的常用数据结构等以便快速上手刷题。例如标准输出控制语句基本数据结构总结标准输出C 的标准输出是 cout用 运算符把需要打印的内容传递给 coutendl 是换行符。inta10;// 输出10coutaendl;// 可以串联输出// 输出Hello, World!coutHello, World!endl;string sabc;// 输出abc 10couts aendl;当然C 语言的 printf 函数也可以用但是 cout 更加方便所以我们一般用 cout。标准输出编程语言的控制语句一般都比较简单最常见的无非就是条件判断和循环下面简单介绍一下。条件判断 if elseinta10;if(a5){couta 5endl;}elseif(a5){couta 5endl;}else{couta 5endl;}// 输出a 5循环 for/whilefor 和 while 都可以用来做循环for 循环一般用于已知循环次数的情况while 循环一般用于未知循环次数的情况。// 0 1 2 3 4for(inti0;i5;i){couti ;}intnum100;// 100 50 25 12 6 3 1while(num0){coutnum ;num/2;}基本数据结构对于标准库的常用数据结构。你在写算法题时可以通过直接使用 vector、set、map 等关键字创建数据结构当然你也要导入相应的头文件。动态数组 vectorvector 是 C 标准库的动态数组。大家以前学 C 语言的时候肯定学过 malloc, int[n] 等方式来创建静态数组但是这种方式非常麻烦而且容易出错。我们做算法题的时候一般使用动态数组 vector而且题目给的输入一般也是 vector 类型。vector 的初始化方法如下#includevectorintn7,m8;// 初始化一个 int 型的空数组 numsvectorintnums;// 初始化一个大小为 n 的数组 nums数组中的值默认都为 0vectorintnums(n);// 初始化一个元素为 1, 3, 5 的数组 numsvectorintnums{1,3,5};// 初始化一个大小为 n 的数组 nums其值全都为 2vectorintnums(n,2);// 初始化一个二维 int 数组 dpvectorvectorintdp;// 初始化一个大小为 m * n 的布尔数组 dp// 其中的值都初始化为 truevectorvectorbooldp(m,vectorbool(n,true));vector 的常用操作#includeiostream#includevectorusingnamespacestd;intmain(){intn10;// 数组大小为 10元素值都为 0vectorintnums(n);// 输出 0 (false)coutnums.empty()endl;// 输出10coutnums.size()endl;// 在数组尾部插入一个元素 20nums.push_back(20);// 输出11coutnums.size()endl;// 得到数组最后一个元素的引用// 输出20coutnums.back()endl;// 删除数组的最后一个元素无返回值nums.pop_back();// 输出10coutnums.size()endl;// 可以通过方括号直接取值或修改nums[0]11;// 输出11coutnums[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(inti0;inums.size();i){coutnums[i] ;}coutendl;}以上就是 C vector 在文章中的常用方法无非就是用索引取元素以及 push_back, pop_back 方法就刷算法题而言这些就够了。因为根据「数组」的特性利用索引访问元素很高效从尾部增删元素也是很高效的而从中间或头部增删元素要涉及搬移数据很低效。双链表 listlist 是 C 标准库中的双向链表容器。初始化方法#includelistusingnamespacestd;intn7;// 初始化一个空的双向链表 lstlistintlst;// 初始化一个大小为 n 的链表 lst链表中的值默认都为 0listintlst(n);// 初始化一个包含元素 1, 3, 5 的链表 lstlistintlst{1,3,5};// 初始化一个大小为 n 的链表 lst其中值都为 2listintlst(n,2);list 的常用方法#includeiostream#includelistusingnamespacestd;intmain(){// 初始化链表listintlst{1,2,3,4,5};// 检查链表是否为空输出falsecoutlst.empty()endl;// 获取链表的大小输出5coutlst.size()endl;// 在链表头部插入元素 0lst.push_front(0);// 在链表尾部插入元素 6lst.push_back(6);// 获取链表头部和尾部元素输出0 6coutlst.front() lst.back()endl;// 删除链表头部元素lst.pop_front();// 删除链表尾部元素lst.pop_back();// 在链表中插入元素autoitlst.begin();// 移动到第三个位置advance(it,2);// 在第三个位置插入 99lst.insert(it,99);// 删除链表中某个元素itlst.begin();// 移动到第二个位置advance(it,1);// 删除第二个位置的元素lst.erase(it);// 遍历链表// 输出1 99 3 4 5for(intval:lst){coutval ;}coutendl;return0;}一般来说当我们想在头部增删元素时会使用双链表因为它在头部增删元素的效率比 vector 高。但我们通过索引访问元素这种场景下我们会使用 vector。队列 queuequeue 是 C 标准库中的队列容器基于先进先出FIFO的原则。队列适用于只允许从一端队尾添加元素、从另一端队头移除元素的场景。#includeiostream#includequeueusingnamespacestd;intmain(){// 初始化一个空的整型队列 qqueueintq;// 在队尾添加元素q.push(10);q.push(20);q.push(30);// 检查队列是否为空输出falsecoutq.empty()endl;// 获取队列的大小输出3coutq.size()endl;// 获取队列的队头和队尾元素输出10 和 30coutq.front() q.back()endl;// 删除队头元素q.pop();// 输出新的队头元素20coutq.front()endl;return0;}栈 stack栈是一种后进先出LIFO的数据结构栈适用于只允许在一端栈顶添加或移除元素的场景。stack 是 C 标准库中的栈容器关于栈的实现原理和使用场景。#includeiostream#includestackusingnamespacestd;intmain(){// 初始化一个空的整型栈 sstackints;// 向栈顶添加元素s.push(10);s.push(20);s.push(30);// 检查栈是否为空输出falsecouts.empty()endl;// 获取栈的大小输出3couts.size()endl;// 获取栈顶元素输出30couts.top()endl;// 删除栈顶元素s.pop();// 输出新的栈顶元素20couts.top()endl;return0;}哈希表 unordered_mapunordered_map 是 C 标准库中的一种哈希表实现它提供了基于键值对key-value的存储提供了常数时间复杂度的查找、插入和删除键值对的操作。#includeunordered_mapusingnamespacestd;// 初始化一个空的哈希表 mapunordered_mapint,stringhashmap;// 初始化一个包含一些键值对的哈希表 mapunordered_mapint,stringhashmap{{1,one},{2,two},{3,three}};unordered_map 的常用方法如下特别注意访问不存在的键会自动插入键值对在 C 的哈希表中如果你访问一个不存在的键它会自动创建这个键对应的值是默认构造的值。这一点和其他语言不同需要格外注意。记住访问值之前要先判断键是否存在否则可能会意外地创建新键导致算法出错。详见下面的示例。#includeiostream#includeunordered_mapusingnamespacestd;intmain(){// 初始化哈希表unordered_mapint,stringhashmap{{1,one},{2,two},{3,three}};// 检查哈希表是否为空输出0 (false)couthashmap.empty()endl;// 获取哈希表的大小输出3couthashmap.size()endl;// 查找指定键是否存在// 注意 contains 方法是 C20 新增的// 输出Key 2 - twoif(hashmap.contains(2)){coutKey 2 - hashmap[2]endl;}else{coutKey 2 not found.endl;}// 获取指定键对应的值若不存在会返回默认构造的值// 输出空字符串couthashmap[4]endl;// 插入一个新的键值对hashmap[4]four;// 获取新插入的值输出fourcouthashmap[4]endl;// 删除键值对hashmap.erase(3);// 检查删除后键 3 是否存在// 输出Key 3 not found.if(hashmap.contains(3)){coutKey 3 - hashmap[3]endl;}else{coutKey 3 not found.endl;}// 遍历哈希表// 输出顺序可能不同// 4 - four// 2 - two// 1 - onefor(constautopair:hashmap){coutpair.first - pair.secondendl;}// 特别注意访问不存在的键会自动创建这个键unordered_mapint,stringhashmap2;// 键值对的数量是 0couthashmap2.size()endl;// 0// 访问不存在的键会自动创建这个键对应的值是默认构造的值couthashmap2[1]endl;// empty stringcouthashmap2[2]endl;// empty string// 现在键值对的数量是 2couthashmap2.size()endl;// 2return0;}哈希集合 unordered_setunordered_set 是 C 标准库中的一种哈希集合实现用于存储不重复的元素常见使用场景是对元素进行去重。#includeiostream#includeunordered_setusingnamespacestd;intmain(){// 初始化哈希集合unordered_setinthashset{1,2,3,4};// 检查哈希集合是否为空输出0 (false)couthashset.empty()endl;// 获取哈希集合的大小输出4couthashset.size()endl;// 查找指定元素是否存在// 输出Element 3 found.if(hashset.contains(3)){coutElement 3 found.endl;}else{coutElement 3 not found.endl;}// 插入一个新的元素hashset.insert(5);// 删除一个元素hashset.erase(2);// 输出Element 2 not found.if(hashset.contains(2)){coutElement 2 found.endl;}else{coutElement 2 not found.endl;}// 遍历哈希集合// 输出顺序可能不同// 1// 3// 4// 5for(constautoelement:hashset){coutelementendl;}return0;}传值和传引用在 C 中函数参数的传递方式主要有两种传值和传引用。理解它们的区别对于编写高效的算法代码至关重要特别是在处理大量数据或需要修改原始数据时。传值Pass by Value传值是指将函数参数的一个副本传递给函数在函数内部对该副本的修改不会影响到原始数据。下面是一个例子#includeiostreamusingnamespacestd;voidmodifyValue(intx){x10;// 只修改副本不会影响原始数据}intmain(){intnum5;modifyValue(num);// 输出5coutAfter modifyValue, num numendl;return0;}在上述代码中num 的值在调用 modifyValue 后并未改变因为传入的是 num 的副本函数内的修改仅影响副本。传引用Pass by Reference传引用是指将实参的地址传递给函数函数可以直接操作原始数据。这意味着对参数的修改会直接影响原始数据。下面是一个例子#includeiostreamusingnamespacestd;voidmodifyReference(intx){x10;// 修改原始数据}intmain(){intnum5;modifyReference(num);// 输出10coutAfter modifyReference, num numendl;return0;}在上述代码中num 的值被修改为 10因为我们传递的是 num 的引用函数内对 x 的修改直接影响了 num。做算法题时的选择根据经验如果是传递基本类型比如 int、bool 等用传值比较多因为这类数据一般不需要在函数内部修改而且复制的开销很小。如果是传递容器数据结构比如 vector、unordered_map 等用传引用比较多因为可以避免复制数据副本的开销而且容器一般需要在函数内部修改。特别注意一个可能出现的问题就是当递归函数的参数中有容器数据结构时千万别使用传值的方式否则每次递归都会创建一个数据副本消耗大量的内存和时间非常容易导致超时或者超内存的错误。总结这些基础语法和数据结构的用法可以帮助我们完成一些基础的算法练习那么有感兴趣的朋友可以私信一起学习。多多关注这是我更新的动力