- 博客(110)
- 收藏
- 关注
原创 STL(五) priority_queue 基本用法 + 模拟实现
priority_queue是一种容器适配器,默认使用vector作为底层容器,通过仿函数控制大堆或小堆的排序方式。文章展示了如何创建大堆和小堆,包括对内置类型和自定义类型的处理,其中自定义类型需要重载比较运算符或自定义仿函数。最后详细讲解了priority_queue的模拟实现过程,包括向上调整和向下调整算法,以及测试用例。关键点包括:默认使用vector容器,通过less/greater仿函数控制堆类型,自定义类型需要重载比较运算符,以及完整的priority_queue类模板实现。
2025-11-20 12:15:20
440
原创 类和对象(下)
本文主要介绍了C++中类的构造函数初始化列表、友元、内部类、匿名对象等特性。初始化列表在对象创建时直接初始化成员变量,效率高于构造函数体内赋值,且必须用于const成员、引用成员和无默认构造的自定义类型成员。友元机制允许外部访问类私有成员,但会破坏封装性。静态成员属于类而非对象,需特殊定义方式。内部类是独立但受限的类,天生是外部类的友元。匿名对象适用于临时使用场景。文章还详细讲解了日期类的实现,包括各种运算符重载和应用场景,最后通过测试验证了日期类的功能。
2025-11-13 11:29:54
780
原创 时间复杂度和空间复杂度
一个算法的时间复杂度分为最好、平均和最坏三种情况,例如在一个数组中查找特定元素,最好情况是在最左边 O(1),最坏情况是在最右边 O(N),平均情况是O(N / 2),而我们实际算时间复杂度时默认是最坏情况!Func1函数的基本操作执行的次数为 2* N^2 + 2*N + 10,但我们其实不用计算精确的执行次数,只需要大概执行次数,这就要用到大O的渐进表示法了。基本操作执行最好N次,最坏执行了(N*(N+1))/2次,通过推导大O阶方法+时间复杂度一般看最坏,时间复杂度为 O(N^2)
2025-11-02 11:56:12
568
原创 C语言实现通讯录
比如我朋友很少,那么空间会大量浪费,但如果我朋友不断增多,原有空间可能不够用,因此很难找到一个合适的值,而我们学过动态内存开辟的相关内容,可以动态调整数组大小!当我们退出通讯录后,下次再打开通讯录时,数据就全部没了,因为我们所有的数据都是保存在内存中的,要想让数据持久化的存储,就需要用到文件了!在初始化完通讯录后,我们要将文件内容加载到通讯录中,使得用户可以直接看到之前的通讯录内容,同时主要加载的过程中要一直判断通讯录空间是否足够,不够要进行扩容。其他接口和静态通讯录没有任何区别,不涉及到扩容的问题。
2025-11-01 15:57:45
646
原创 C语言实现扫雷游戏
define ROW 9 //原先行#define COL 9 //原先列#define ROWS ROW+2 //加一圈之后的行#define COLS COL+2 //加一圈之后的列#define EASY_COUNT 10 //布置雷的个数。
2025-10-31 17:53:11
306
原创 C语言实现三子棋游戏
1.不管是玩家还是电脑,输赢规则是一样的,就是同样的3个棋子连成一条线,而连成一条线有4种情况,横着一条线 / 竖着一条线 / 正对角线一条线 / 副对角线一条线,同时我们还要判断有没有棋子,防止出现"没一个棋子也是一条线"的情况。3.如果没有出现三个棋子一条线的情况,那可能是棋盘满了,也要做一下判断,如果都不是,那就返回'C',表示继续下棋。我们提前约定好,玩家下棋用 '#',电脑下棋用 '*','Q'代表平局,'C'代表没有决出胜负,继续下棋。开始时,棋盘什么都没有,我们用空格代表该位置没有棋子。
2025-10-31 12:06:02
290
原创 详解C语言文件操作
磁盘上的文件是文件,但是在程序设计中,从文件的功能角度来分类,我们一般谈的文件有两种:程序文件、数据文件• 程序文件:包括源程序文件(后缀为.c),目标文件(windows环境后缀为.obj),可执行程序(windows环境后缀为.exe)• 数据文件:文件的内容不一定是程序,而是程序运行时读写的数据,比如程序运行需要从中读取数据的文件或者输出内容的文件本文讨论的是数据文件。在以前各章所处理数据的输入输出都是以终端为对象的,即从终端的键盘输入数据,运行结果显示到显示器上。
2025-10-24 17:00:16
565
原创 动态内存管理
/开辟大小为size字节的空间• 如果开辟成功,返回一个指向开辟好空间的指针• 如果开辟失败,返回NULL指针,因此malloc的返回值一定要做检查• 返回值的类型是 void*,所以malloc函数并不知道开辟空间的类型,具体在使用的时候使用者自己来决定。• 如果参数 size 为0,malloc的行为是标准未定义的,取决于编译器。
2025-10-24 16:59:43
702
原创 数据的存储
前面我们已经学习了基本的内置类型,以及他们所占存储空间的大小。char //字符数据类型short //短整型int //整形long //长整型long long //更长的整形float //单精度浮点数double //双精度浮点数注意:C语言没有字符串类型!!!类型的意义:1. 使用这个类型开辟内存空间的大小(大小决定了使用范围)。2. 如何看待内存空间的视角。类型的基本归类。
2025-10-24 16:58:54
683
原创 详解C语言操作符
eg1: 由于a和b都不足一个int,因此在判断相等时会整形提升,a的二进制和c的二进制最高位都是1,因此整形提升前面都补1,最后得到的值和原先值都不一样,所以程序最后打印c。例如,要计算a + b,由于a和b都是char类型,不足4字节,先要提升为int,然后相加得到一个int值,由于要存放到 char 类型的 c 变量里,要发生截断。一般就是int的字节长度,同时也是CPU的通用寄存器的长度。逗号表达式,就是逗号隔开的多个表达式,从左向右执行,整个表达式的结果是最后一个表达式的结果。
2025-10-24 16:57:55
884
原创 详解C语言数组
1. 数组是使用下标来访问的,下标是从0开始。2. 数组的大小可以通过计算得到 sizeof(arr) / sizeof(arr[0])
2025-10-24 16:56:57
267
原创 详解C语言函数
•1. 告诉编译器有一个函数叫什么,参数是什么,返回类型是什么。但是具体是不是存在,函数声明决定不了。2. 函数的声明一般出现在函数的使用之前。要满足先声明后使用。3. 函数的声明一般要放在头文件中的。函数的定义是指函数的具体实现,交待函数的功能实现。
2025-10-24 16:55:33
697
原创 分支和循环语句
C语句可分为以下五类:1.表达式语句2.函数调用语句3.控制语句4.复合语句5.空语句本文介绍的是控制语句,控制语句用于控制程序的执行流程,以实现程序的各种结构方式,它们由特定的语句定义符组成,C语言有九种控制语句可分成以下三类:1.条件判断语句也叫分支语句:if语句、switch语句;2.循环执行语句:do while语句、while语句、for语句;3.转向语句:break语句、goto语句、continue语句、
2025-10-24 16:54:26
699
原创 数据结构(九) AVL树
AVL树是一种自平衡二叉搜索树,通过旋转操作保持任意节点的左右子树高度差不超过1。文章详细介绍了AVL树的概念、结构实现(采用三叉链存储节点)以及四种旋转操作(LL右旋、RR左旋、LR左右双旋、RL右左双旋)。重点阐述了插入操作时如何更新平衡因子并进行相应旋转调整,确保树始终保持平衡。最后提供了完整的C++实现代码,包括节点结构、旋转操作和平衡检查方法。AVL树通过这种平衡机制,保证了搜索、插入和删除操作的时间复杂度均为O(log n)。
2025-01-06 23:20:05
1015
原创 STL(三) list模拟实现
本文详细介绍了带头双向循环链表的C++实现方法,主要包括以下内容: 链表节点结构使用模板类设计,包含数据成员和前驱/后继指针。 实现了构造函数、析构函数、拷贝构造等基本功能。 重点实现了迭代器机制: 正向迭代器通过封装节点指针并重载运算符实现 反向迭代器通过适配正向迭代器实现 支持const和非const两种版本 实现了常用的链表操作: 插入/删除元素 头插/尾插/头删/尾删 清空链表等 提供了完整的测试代码,验证了各种功能。 扩展实现了通用的STL容器遍历函数,展示了模板编程的强大。
2024-12-26 00:27:38
1242
原创 STL(四) stack 与 queue 基本用法 + 模拟实现
本文介绍了C++中容器适配器stack和queue的使用及其底层实现。stack和queue作为容器适配器,通过封装底层容器(如deque、list等)提供特定的数据操作接口。文章详细讲解了它们的基本用法、底层容器选择标准,以及deque作为默认底层容器的原因(高效双端操作、内存利用等)。同时,还实现了stack和queue的简单模拟版本,展示了容器适配器的工作原理。值得注意的是,这些适配器不直接存储数据,而是通过指定底层容器来提供特定的数据结构功能。
2024-12-25 19:59:32
839
原创 字符串函数
errno 是C语言的一个全局变量, 当库函数执行报错时,就会将报错信息以数字1, 2, 3等等的方式设置进errno, 但是数字是不方便人看的,因此我们可以用 strerror 库函数将错误码对应的错误信息以字符串方式表示出来。str2长度是 < str2长度的,因此减完之后是负数,但size_t是无符号的,所以返回值会被解析成一个很大的数字,因此就匹配到了else。
2024-12-25 11:11:06
1054
原创 数据结构(八) 二叉搜索树
本文详细介绍了二叉搜索树(二叉排序树/二叉查找树)的概念、特点及其实现方式。主要内容包括: 二叉搜索树概念:具有左子树值小于根节点、右子树值大于根节点特性的二叉树,其中序遍历结果为升序序列。 key模型实现: 核心操作:插入(递归/非递归)、查找(递归/非递归)、删除(递归/非递归) 辅助功能:中序遍历、构造/析构函数、拷贝构造、赋值重载 删除操作重点处理了三种情况:叶子节点、单子树节点、双子树节点(使用替换法) key-value模型实现: 扩展为字典结构,支持键值对存储 可应用于字典查询和词频统计等场景
2024-12-24 22:31:26
1121
原创 详解C++模板篇
C++模板是泛型编程的基础,通过函数模板和类模板实现代码复用。函数模板通过参数化类型生成特定版本,支持隐式和显式实例化。类模板实例化时需指定类型参数,成员函数定义需加模板前缀。模板特化(全特化/偏特化)可针对特定类型定制实现。模板分离编译需将声明与定义放在同一文件,避免链接错误。模板虽提高灵活性但可能导致代码膨胀和编译错误定位困难。非类型模板参数支持编译期常量配置,增强性能优势。
2024-12-24 10:11:51
655
原创 数据结构(七) 堆
本文介绍了堆的概念与实现。堆是完全二叉树,分为大根堆和小根堆,采用顺序结构存储。详细讲解了堆的初始化、插入、删除、获取堆顶等基本操作,重点阐述了向上调整和向下调整两种算法。通过示例代码演示了堆排序原理和Top-K问题的应用场景。堆排序时间复杂度为O(NlogN),Top-K问题最优解为O(NlogK)。文中提供完整的堆实现代码,并包含文件数据处理的真实案例。堆结构在排序和前K大/小元素查找等问题中具有重要应用价值。
2024-12-23 11:29:43
947
原创 数据结构(六) 普通二叉树
●二叉树所有节点的度都小于等于2,也就是说,每个节点最多有两个2孩子,可以有1个,也可以没有,下面这些树都是二叉树●二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树● 满二叉树:最后一层的节点没有任何孩子节点外,其余每个节点都有两个孩子节点● 完全二叉树:假设树有k层,前k-1层每个节点都有2个孩子,第k层可以不满,但要求节点从左到右是连续的。
2024-12-23 11:26:57
689
1
原创 数据结构(五) 队列
●只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表●进行插入操作的一端称为队尾;进行删除操作的一端称为队头●队列具有先进先出 FIFO(First In First Out)●出队列顺序和入队列顺序是完全一样的。
2024-12-23 10:23:51
507
原创 数据结构(四) 栈
●一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作●进行数据插入和删除操作的一端称为栈顶,另一端称为栈底●栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则●压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。●出栈:栈的删除操作叫做出栈。出数据也在栈● 入栈顺序为1、2、3、4,出栈顺序不一定为4、3、2、1,因为入栈的同时可以出栈,比如入1,出1,接着入2、3、4,再出4、3、2,最后出栈的顺序为1、4、3、2。
2024-12-23 10:22:09
416
原创 数据结构(三) 带头双向循环链表
目录双链表链表结构双链表打印双链表查找双链表创建新节点双链表初始化双链表判空双链表头插双链表尾插编辑双链表头删双链表尾删双链表任意位置之前插入双链表任意位置删除双链表销毁双链表测试● 链表有三种分类方式,带头/不带头,单向/双向,循环/不循环,共8种组合带头/不带头,"头"指的是哨兵位的头节点/虚拟头结点,不存储有效数据,哨兵位头节点的next指针指向头节点,带头链表最明显的优势是可以将链表为空和链表不为空的一些情况统一处理,不需要单独判断,使得操作更简单。单向/双向,单向链表中,每个节点只有1个指针域,
2024-12-23 10:17:58
1114
原创 数据结构(二) 单链表
链表的每个元素是在内存中的位置是随机的,而数据结构的核心工作是组织管理数据,因此要求我们必须能从前一个元素找到下一个元素,因此链表是一个个节点,每个节点不仅存储数据,还存储了下一个节点的地址,因此每个节点类型是结构体,包含数据域和指针区两部分,最后一个节点的指针域设置为NULL即可。● 如果链表不为空,那么尾插操作应该先找到尾,而尾的特点是它的next指针为空,因此采用while循环找尾,找到尾之后,将尾的next赋值成newnode。顺序表的每个元素挨着存放在内存中,因此可以直接通过下标访问整个顺序表。
2024-12-23 10:16:11
986
原创 数据结构(一) 顺序表
● 顺序表分为静态顺序表动态顺序表,静态顺序表就是一次性把空间开好,后续不再调整,但问题是开多大空间呢,开小了可能不够用,开大了容易造成空间浪费,因此静态顺序表用的比较少;● a指针保存动态申请的连续空间的起始地址,size表示顺序表中元素的个数,capacity表示顺序表当前的容量。● 头删是删除第一个位置元素,可以将后面所有元素都往前挪动一位,最后覆盖第一个元素。● 要头插,就得将顺序表所有元素向后挪动1位,给要插入的元素腾出空间。● 将插入位置及其之后的所有元素向后挪动1位,腾出插入的空间。
2024-12-23 10:13:20
326
原创 STL(三) list基本用法
本文介绍了C++ STL中list容器的特性和常用操作。list是一种基于双向链表的序列容器,支持高效的元素插入和删除,但不支持随机访问。文章详细讲解了list的构造方式、迭代器类型(包括正向/反向、普通/const迭代器)、容量操作(empty、size、resize)以及元素访问(front、back)。重点阐述了list特有的排序功能,包括内置类型和自定义类型的升序/降序实现,介绍了函数指针、仿函数等多种排序方式。此外,还介绍了unique去重、remove/remove_if删除元素等
2024-12-17 10:30:24
1187
原创 STL(二) vector基本用法
本文介绍了C++中的vector容器及其常见用法。vector是STL中的动态数组,支持高效随机访问和尾部操作。文章详细讲解了vector的创建方式、迭代器使用、容量操作、元素访问和修改方法。重点讨论了vector迭代器失效问题,包括扩容、插入和删除操作导致的失效情况,并给出解决方案:通过reserve预分配空间、利用insert/erase返回值更新迭代器。最后强调修改vector大小会导致end()迭代器失效,需要重新获取。
2024-12-15 21:35:52
964
原创 STL(一) string模拟实现
本文详细介绍了C++中string类的实现方法,重点包括:1.基础结构设计,使用命名空间封装;2.关键成员函数实现,包括构造/析构函数、拷贝控制、运算符重载等;3.迭代器实现和比较运算符;4.字符串操作函数如reserve/resize/push_back等;5.流操作符重载。通过测试用例验证了实现的正确性,完整展示了如何从零构建一个功能完善的string类。实现中采用了现代C++的swap技巧等优化方法,并特别注意了异常安全性。
2024-12-15 11:22:12
662
1
原创 生产者-消费者模型
● 计算机中的生产者和消费者本质都是线程/进程● 生产者和消费者不直接通讯,而是通过一段内存缓冲区进行通讯● 生产者生产完数据直接放在内存缓冲区,不必等待消费者消费;消费者需要消费数据直接从内存缓冲区中取,不必等待生产者生产● 生产者和消费者只有1个,是单生产-单消费模型;生产者和消费者有多个,是多生产-多消费模型● 缓冲区满了,生产者会阻塞等待,不会再生产数据了,直到消费者消费了数据;缓冲区空了,消费者会阻塞等待,不会再消费数据了,直到生产者生产了数据。
2024-12-14 11:04:14
1091
原创 Linux中的线程
显然票数是公共资源,可以被多个执行流同时访问,而多个执行流同时访问公共资源显然出现了问题,因此我们需要把公共资源保护起来,使得任何一个时刻,只允许一个线程正在访问公共资源,此时公共资源就叫做临界资源!而我们的代码中只有一部分代码会去访问临界资源的,进程中访问临界资源的代码叫做临界区。
2024-12-12 14:17:49
982
原创 Linux中的信号
信号的默认处理和忽略动作,第二个参数都是大写的英文单词,本质就是宏,将0/1强制类型转化成函数指针类型,所以使用signal函数时,系统会先判断参数是否为0/1,如果为0/1,执行默认处理动作/对信号进行忽略,不为0/1,才去执行回调函数,进行自定义捕捉!
2024-12-05 22:41:30
725
原创 进程间通信
● 父进程要向管道派发任务,就需要将管道管理起来,比如向哪个管道派发,不想派发时关掉哪个管道的写端,关闭写端后回收对应的子进程等,因此要对管道进行"描述",也就是创建管道类,再进行组织,也就是使用vector容器将诸多管道对象管理起来,方便增删查改。● A进程和B进程都往消息队列中写数据块,而通信时A读取的是B进程写入的数据块,B进程读取的是A进程写入的数据块,因此数据块是要有类型的,类型本质是宏定义的数字常量,比如用1表示A进程写入的数据块,2表示B进程写入的数据块。
2024-12-02 11:28:37
643
原创 自定义类型: 结构体、枚举 、联合
●定义全局变量并初始化//声明结构体类型的同时初始化变量(定义+赋初值)struct Stu//名字int age;//年龄//性别//学号//全局变量//全局变量int main()return 0;●定义局部变量并初始化struct Stu//名字int age;//年龄//性别//学号int main()return 0;● 结构体的嵌套定义int x;char ch;struct Stu//名字int age;//年龄//性别//学号。
2024-11-30 23:15:02
1115
原创 详解预处理篇
许多C 的编译器提供了一种能力,允许在命令行中定义符号。用于启动编译过程。例如:当我们根据同一个源文件要编译出不同的一个程序的不同版本的时候,这个特性有点用处。(假定某个程序中声明了一个某个长度的数组,如果机器内存有限,我们需要一个很小的数组,但是另外一 个机器内存大,我们需要一个数组能够大些)条件编译在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件 编译指令。简单来说,满足条件就编译,不满足条件就不进行编译,这就是条件编译!
2024-11-27 20:45:22
712
原创 指针笔试题+面试题
●数组名一般都表示数组首元素的地址, 两种情况除外: sizeof(数组名) 表示整个数组的大小 (注意没有进行+/-等运算);&数组名取出的是整个数组的地址●指针(地址)的大小固定是4/8个字节,32位平台下是4个字节,64位平台下是8个字节。
2024-11-23 11:19:23
786
原创 详细剖析指针篇
我们知道,排序的对象可能会有各种类型,比如整形,浮点型,其他自定义类型(结构体等),而实现qsort库函数的作者并不知道你将来要使用qsort排序什么样类型的数据,因此将qsort第四个参数设置成了函数指针,由用户自己传入比较规则的函数, 实现排序, 并且qsort默认是升序,如果要排降序,将自定义函数的两个参数的前后顺序换一下即可!add函数的类型是 int (int, int), pf是一个指针变量,所以(*pf), 去掉*和pf, 剩余的应该是pf指向的内容的类型,也就是int (int, int)
2024-11-19 10:49:26
1028
1
原创 【动态规划七】背包问题
1049. 最后一块石头的重量 II - 力扣(LeetCode)1.题目解析之前我们已经在博客中讲解了"最后一块石头的重量 I", 每次选两块最重的石头,而本题每次随机选两个石头进行碰撞,如果最后还剩余了一块石头,那么求最终剩余石头的最小重量,如果没有剩余石头,那么返回02.算法分析本题的难点仍然在于如何把原问题进行转化,假设给定的数组元素是[a, b, c, d, e]每次随机选两个石头进行碰撞, 可以发现最终结果无非是给原始数组元素添加上正负号,使得最终。
2024-05-24 11:45:55
898
原创 【动态规划六】两个数组的dp问题
上图是根据最后一个位置的状态划分问题得到的状态转移方程,但显然p[j]=='*'时,还需要一层for循环求解, 因此时间复杂度是O(N^3), 因此我们可以进一步优化, 将p[j]=='*'时的状态转移方程用若干个有限的状态表示。dp[i][j]: s1中 [1, i] 区间内的字符串以及 s2[1, j] 区间内的字符串,能拼接成 s3[1, i+j] 区间内的字符串。这两个字符串就是可以匹配的,让第二个字符串的'*'匹配空串,'a'匹配'a', '*'匹配"bcde", 'f'匹配'f'即可。
2024-05-20 08:35:19
1256
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人
RSS订阅