- 博客(102)
- 收藏
- 关注
原创 实现AVL树
2 .当我们把最⼩平衡⼦树调整平衡之后,那么这棵⼦树的⾼度就会减 ,向上传递的过程中,会让 整个路径⾥⾯的平衡因⼦都向0靠近⼀位,原来的-2会变成-1,原来的2会变成1,整棵树就变得平衡了。左图是⼀棵平衡⼆叉树,右图不是⼀棵平衡⼆叉树。最⼩不平衡⼦树是以49为根的⼦树,引起不平衡的原因是49的左孩⼦的右⼦树上插⼊⼀个新的结点,因此需要左旋⼀次,然后右旋⼀次。最⼩不平衡⼦树是以49为根的⼦树,引起不平衡的原因是49的右孩⼦的左⼦树上插⼊⼀个新的结点,因此需要右旋⼀次,然后再左旋⼀次。当然,反过来也是可以的)
2025-01-06 23:20:05
931
原创 list模拟实现
● 之前学习的迭代器本质是原生指针,但list的迭代器是对节点指针封装的类,因为迭代器提供的统一操作是++/--来访问前一个或者后一个节点,但是list是链表,因此我们要将++/--实现成类的运算符重载函数,就可以让++/--实现访问前一个/后一个节点的效果。● 由于正向普通迭代器和正向const迭代器的类型不同,是需要实现两个类的,但我们可以给类添加额外两个模版参数,就可以控制是普通迭代器还是const迭代器。● 反向迭代器可以用正向迭代器去适配,反向迭代器的++/--本质就是正向迭代器的--/++
2024-12-26 00:27:38
1151
原创 stack与queue与priority_queue与deque
但是,deque有一个致命缺陷:不适合遍历,因为在遍历时,deque的迭代器要频繁的去检测其是否移动到某段小空间的边界,导致效率低下,而序列式场景中,可能需要经常遍历,因此在实际中,需要线性结构时,大多数情况下优先考虑vector和list,deque的应用并不多,而目前能看到的一个应用就是,STL用其作为stack和queue的底层数据结构。与vector比较,deque的优势是:头部插入和删除时,不需要搬移元素,效率特别高,而且在扩容时,也不需要搬移大量的元素,因此其效率是必vector高的。
2024-12-25 19:59:32
767
原创 字符串函数
errno 是C语言的一个全局变量, 当库函数执行报错时,就会将报错信息以数字1, 2, 3等等的方式设置进errno, 但是数字是不方便人看的,因此我们可以用 strerror 库函数将错误码对应的错误信息以字符串方式表示出来。str2长度是 < str2长度的,因此减完之后是负数,但size_t是无符号的,所以返回值会被解析成一个很大的数字,因此就匹配到了else。
2024-12-25 11:11:06
970
原创 二叉搜索树
二叉搜索树/二叉排序树/二叉查找树,它或者是一棵空树,或者是具有以下性质的二叉树:1.若它的左子树不为空,则左子树上所有节点的值都小于根节点的值2.若它的右子树不为空,则右子树上所有节点的值都大于根节点的值3.它的左右子树也分别为二叉搜索树特点● 二叉搜索树的中序遍历是升序● 当二叉搜索树接近满二叉树时,查找效率最高,为O(logN);当二叉搜索树时单支二叉树时,查找效率最低,为O(N)
2024-12-24 22:31:26
969
原创 详解C++模板篇
目录泛型编程函数模板函数模板使用函数模板原理函数模板实例化模板参数的匹配原则类模板类模板使用类模板实例化非类型模板参数模板特化函数模板特化类模板特化模板的分离编译如何实现一个通用的交换函数呢?使用函数重载虽然可以实现,但是有一下几个不好的地方:1. 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函数2. 代码的可维护性比较低,一个出错可能所有的重载均出错那能否告诉编译器一个模子,让编译器根据不同的类型利用该模子来生成代码呢?在C++中,存在这样的模子的,这个模
2024-12-24 10:11:51
557
原创 二叉树介绍和操作
●二叉树所有节点的度都小于等于2,也就是说,每个节点最多有两个2孩子,可以有1个,也可以没有,下面这些树都是二叉树●二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树● 满二叉树:最后一层的节点没有任何孩子节点外,其余每个节点都有两个孩子节点● 完全二叉树:假设树有k层,前k-1层每个节点都有2个孩子,第k层可以不满,但要求节点从左到右是连续的。
2024-12-23 11:26:57
600
原创 队列介绍和实现
●只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表●进行插入操作的一端称为队尾;进行删除操作的一端称为队头●队列具有先进先出 FIFO(First In First Out)●出队列顺序和入队列顺序是完全一样的。
2024-12-23 10:23:51
423
原创 栈介绍和实现
●一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作●进行数据插入和删除操作的一端称为栈顶,另一端称为栈底●栈中的数据元素遵守后进先出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
325
原创 带头双向循环链表
目录链表结构打印查找创建新节点初始化判空头插尾插头删尾删任意位置之前插入任意位置删除销毁测试● 链表有三种分类方式,带头/不带头,单向/双向,循环/不循环,共8种组合带头/不带头,"头"指的是哨兵位的头节点/虚拟头结点,不存储有效数据,哨兵位头节点的next指针指向头节点,带头链表最明显的优势是单向/双向,单向链表中,每个节点只有1个指针域,存储的是下一个节点的地址,因此单链表只能朝一个方向遍历,而双向链表中,每个节点有2个指针域,分别存储的是上一个节点的地址和下一个节点的地址,因此双链表可以朝两个方向遍
2024-12-23 10:17:58
903
原创 实现单链表
链表的每个元素是在内存中的位置是随机的,而数据结构的核心工作是组织管理数据,因此要求我们必须能从前一个元素找到下一个元素,因此链表是一个个节点,每个节点不仅存储数据,还存储了下一个节点的地址,因此每个节点类型是结构体,包含数据域和指针区两部分,最后一个节点的指针域设置为NULL即可。● 如果链表不为空,那么尾插操作应该先找到尾,而尾的特点是它的next指针为空,因此采用while循环找尾,找到尾之后,将尾的next赋值成newnode。顺序表的每个元素挨着存放在内存中,因此可以直接通过下标访问整个顺序表。
2024-12-23 10:16:11
912
原创 实现顺序表
● 顺序表分为静态顺序表动态顺序表,静态顺序表就是一次性把空间开好,后续不再调整,但问题是开多大空间呢,开小了可能不够用,开大了容易造成空间浪费,因此静态顺序表用的比较少;● a指针保存动态申请的连续空间的起始地址,size表示顺序表中元素的个数,capacity表示顺序表当前的容量。● 头删是删除第一个位置元素,可以将后面所有元素都往前挪动一位,最后覆盖第一个元素。● 要头插,就得将顺序表所有元素向后挪动1位,给要插入的元素腾出空间。● 将插入位置及其之后的所有元素向后挪动1位,腾出插入的空间。
2024-12-23 10:13:20
259
原创 list使用
● list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代● list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素● list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高 效。● 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率 更好。
2024-12-17 10:30:24
1128
原创 vector使用
● vector是表示可变大小数组的序列容器。● 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素 进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。● 本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小 为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。
2024-12-15 21:35:52
899
原创 string模拟实现
● 直接传值传参,tmp就是临时对象,将临时对象和当前对象的成员进行交换,临时对象出作用域会调用析构函数释放底层的旧空间,临时对象也会自动销毁。● 直接构造一个临时对象,将临时对象和当前对象的成员进行交换,临时对象出作用域会调用析构函数释放底层的旧空间,临时对象也会自动销毁。● 如果n > 当前容量,就申请一块新空间,将字符串内容拷贝到新空间,释放旧空间,更新当前对象的成员。● 开辟一块新空间,将字符串内容拷贝到新空间,释放旧空间,更新当前对象的成员。● 当n
2024-12-15 11:22:12
515
1
原创 生产者-消费者模型
● 计算机中的生产者和消费者本质都是线程/进程● 生产者和消费者不直接通讯,而是通过一段内存缓冲区进行通讯● 生产者生产完数据直接放在内存缓冲区,不必等待消费者消费;消费者需要消费数据直接从内存缓冲区中取,不必等待生产者生产● 生产者和消费者只有1个,是单生产-单消费模型;生产者和消费者有多个,是多生产-多消费模型● 缓冲区满了,生产者会阻塞等待,不会再生产数据了,直到消费者消费了数据;缓冲区空了,消费者会阻塞等待,不会再消费数据了,直到生产者生产了数据。
2024-12-14 11:04:14
994
原创 Linux中的线程
显然票数是公共资源,可以被多个执行流同时访问,而多个执行流同时访问公共资源显然出现了问题,因此我们需要把公共资源保护起来,使得任何一个时刻,只允许一个线程正在访问公共资源,此时公共资源就叫做临界资源!而我们的代码中只有一部分代码会去访问临界资源的,进程中访问临界资源的代码叫做临界区。
2024-12-12 14:17:49
925
原创 Linux中的信号
信号的默认处理和忽略动作,第二个参数都是大写的英文单词,本质就是宏,将0/1强制类型转化成函数指针类型,所以使用signal函数时,系统会先判断参数是否为0/1,如果为0/1,执行默认处理动作/对信号进行忽略,不为0/1,才去执行回调函数,进行自定义捕捉!
2024-12-05 22:41:30
655
原创 进程间通信
● 父进程要向管道派发任务,就需要将管道管理起来,比如向哪个管道派发,不想派发时关掉哪个管道的写端,关闭写端后回收对应的子进程等,因此要对管道进行"描述",也就是创建管道类,再进行组织,也就是使用vector容器将诸多管道对象管理起来,方便增删查改。● A进程和B进程都往消息队列中写数据块,而通信时A读取的是B进程写入的数据块,B进程读取的是A进程写入的数据块,因此数据块是要有类型的,类型本质是宏定义的数字常量,比如用1表示A进程写入的数据块,2表示B进程写入的数据块。
2024-12-02 11:28:37
565
原创 自定义类型: 结构体、枚举 、联合
●定义全局变量并初始化//声明结构体类型的同时初始化变量(定义+赋初值)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
1038
原创 详解预处理篇
许多C 的编译器提供了一种能力,允许在命令行中定义符号。用于启动编译过程。例如:当我们根据同一个源文件要编译出不同的一个程序的不同版本的时候,这个特性有点用处。(假定某个程序中声明了一个某个长度的数组,如果机器内存有限,我们需要一个很小的数组,但是另外一 个机器内存大,我们需要一个数组能够大些)条件编译在编译一个程序的时候我们如果要将一条语句(一组语句)编译或者放弃是很方便的。因为我们有条件 编译指令。简单来说,满足条件就编译,不满足条件就不进行编译,这就是条件编译!
2024-11-27 20:45:22
665
原创 指针笔试题+面试题
●数组名一般都表示数组首元素的地址, 两种情况除外: sizeof(数组名) 表示整个数组的大小 (注意没有进行+/-等运算);&数组名取出的是整个数组的地址●指针(地址)的大小固定是4/8个字节,32位平台下是4个字节,64位平台下是8个字节。
2024-11-23 11:19:23
700
原创 详细剖析指针篇
我们知道,排序的对象可能会有各种类型,比如整形,浮点型,其他自定义类型(结构体等),而实现qsort库函数的作者并不知道你将来要使用qsort排序什么样类型的数据,因此将qsort第四个参数设置成了函数指针,由用户自己传入比较规则的函数, 实现排序, 并且qsort默认是升序,如果要排降序,将自定义函数的两个参数的前后顺序换一下即可!add函数的类型是 int (int, int), pf是一个指针变量,所以(*pf), 去掉*和pf, 剩余的应该是pf指向的内容的类型,也就是int (int, int)
2024-11-19 10:49:26
968
1
原创 【动态规划七】背包问题
1049. 最后一块石头的重量 II - 力扣(LeetCode)1.题目解析之前我们已经在博客中讲解了"最后一块石头的重量 I", 每次选两块最重的石头,而本题每次随机选两个石头进行碰撞,如果最后还剩余了一块石头,那么求最终剩余石头的最小重量,如果没有剩余石头,那么返回02.算法分析本题的难点仍然在于如何把原问题进行转化,假设给定的数组元素是[a, b, c, d, e]每次随机选两个石头进行碰撞, 可以发现最终结果无非是给原始数组元素添加上正负号,使得最终。
2024-05-24 11:45:55
838
原创 【动态规划六】两个数组的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
1161
原创 【动态规划五】回文串问题
将dp表里面所有的值都初始化成1, 因此dp[i] += dp[i-1] 即可。如果是暴力解法,只需要先固定下标i, 然后j从i的位置开始往后一路枚举即可,下一次i++, j无需回到0号下标,因为[i, j]与[j, i]本质是同一个子串,j直接从i位置开始枚举即可, 因此我们就把状态表示定义成二维的~s[i] == s[j]时,i + 1 == j 的情况可以合并到 i + 1 > j的情况中,因为i + 1 > j 时用到的dp[i+1][j-1]本质是下三角元素,直接等于0,不影响结果的正确性。
2024-05-15 22:40:10
793
原创 用红黑树封装出map与set
map与set的底层都是红黑树,所以本篇文章就分享如何用同一颗红黑树封装出map与set上述迭代器的实现很多与list模拟实现是一样的:关键的地方在于迭代器++与迭代器--如何实现,由于map/set的遍历是中序遍历,因此迭代器++的本质就是要找二叉树中序遍历过程中当前节点的下一个节点, 而中序遍历的顺序是: 左子树 根 右子树,
2024-05-15 19:49:17
711
原创 优先级队列(堆)
1046. 最后一块石头的重量 - 力扣(LeetCode)1.题目解析每次选取最重的两块石头,两两碰撞,重量小的粉碎,重量大的石头剩余重量为原始重量-重量小的石头的重量,最终如果剩一块石头,就返回该石头的重量;如果没有剩石头,就返回02.算法分析用堆(优先级队列) 模拟即可3.算法代码public://默认大堆heap.pop();heap.pop();
2024-05-14 17:32:23
1033
原创 【动态规划四】子序列问题
给定一个数组,数组每个元素是一个数对[left, right], left 是严格 < right 的,[a, b], [c, d], [e, f]要能构成数对链,要求b < c, d < e, 求能构成的最长数对链的长度。ps:开始是定义的dp[i], 但是推不出状态转移方程, 因为只知道子序列的个数,但是无法确定具体的斐波那契式的子序列,也就无法根据dp[i]前面的值推导出dp[i]~dp[i][j]:以 i 位置 以及 j 位置 为结尾的所有子序列中,最长的等差序列的长度。
2024-05-13 09:40:02
1057
原创 队列 + 宽搜(BFS)
二叉树根节点从1开始编号,创建一个队列,队列中存储的是<节点指针,节点编号>, 如果算上空节点的话那么二叉树就是满的,所以如果某个节点编号是x,那么左孩子节点编号是2*x, 右孩子节点编号是2*x+1, 每一层的长度就是 最右节点编号 - 最左节点编号 + 1, 而我们可以直接用vector来模拟队列~树的层序遍历是典型要用到队列的场景,因为上一层遍历完之后,遍历下一层是从左到右,也就是要先遍历上一层最左节点的孩子,因此符合先进先出的原则,采用队列!102. 二叉树的层序遍历 - 力扣(LeetCode)
2024-05-11 17:34:00
999
原创 【动态规划三】子数组系列
f[i]:以 i 位置为结尾的所有子数组中,最后呈现 "上升" 状态下的最长的湍流子数组的长度。g[i]:以 i 位置为结尾的所有子数组中,最后呈现 "下降" 状态下的最长的湍流子数组的长度。数组中相邻元素对之间的大小关系相反,这就是湍流数组,题目要求返回数组中最长的湍流子数组的长度。将dp表里面所有的值都初始化成1, 因此dp[i] += dp[i-1] 即可。dp[i]: [0, i]区间内的字符串, 能否被字典中的单词拼接而成。dp[i] 表示 以 i 位置元素为结尾的所有子数组的最大和。
2024-05-07 09:38:31
934
1
原创 【算法系列】栈
目录leetcode题目一、删除字符串中的所有相邻重复项二、比较含退格的字符串三、基本计算器 II四、字符串解码五、验证栈序列六、有效的括号七、最小栈八、逆波兰表达式求值九、用栈实现队列十、用队列实现栈1047. 删除字符串中的所有相邻重复项 - 力扣(LeetCode)https://leetcode.cn/problems/remove-all-adjacent-duplicates-in-string/1.题目解析2.算法分析显然该题是要用栈,遍历字符串,当遍历到的元素和栈顶元素一样时,就让栈顶元素出
2024-05-05 20:31:45
441
原创 【动态规划二】简单多状态dp问题
【动态规划一】斐波拉契数列模型 && 路径问题-优快云博客状态表示是指dp表(一维数组/二维数组)里面的值所表示的含义如何得到状态表示?1.1 题目要求1.2 经验 + 题目要求1.3 分析问题的过程中,发现重复子问题。面试题 17.16. 按摩师 - 力扣(LeetCode)给定一个数组,数组元素表示预约时间,不能选择数组相邻元素,求最长的预约时长dp[i]: 选择到 i 位置的时候,此时的最长预约时长--->i位置可以选,可以不选,进一步细化。
2024-05-04 17:31:20
1046
原创 【算法系列】字符串
字符串最后一个单词的长度_牛客题霸_牛客网 (nowcoder.com)1.题目解析字符串中的单词以空格分割,求出字符串最后一个单词的长度2.算法分析直接调用string类的接口rfind, 从右向左找到第一个空格位置pos即可,用字符串总长度-pos-13.算法代码int main()string s;return 0;
2024-05-03 19:07:10
1428
1
原创 【动态规划一】斐波拉契数列模型 && 路径问题
状态表示是指dp表(一维数组/二维数组)里面的值所表示的含义如何得到状态表示?1.1 题目要求1.2 经验 + 题目要求1.3 分析问题的过程中,发现重复子问题。
2024-05-02 15:48:30
812
原创 【算法系列】哈希表
1.存储数据的容器2.需要快速查找数据时,用哈希表3.当题目中给定的字符串或者数组只包含小写字母或数据范围是0~100/1000等等时,可以用数组模拟哈希表4.当数据范围是负数到正数时,不建议用数组模拟哈希表,因为还要加一个数转化之后进行映射,建议直接使用STL容器leetcode题目一、两数之和1. 两数之和 - 力扣(LeetCode)1.题目解析给定数组与target, 返回和为target的两个元素下标2.算法分析解法一: 暴力枚举。
2024-05-01 19:29:35
839
1
原创 【算法系列】链表
如上图所示,要将cur插入到两个节点之间,那么①与②的顺序就不能颠倒,但是如果定义了一个指针变量next,就完全不用考虑链接顺序了!①②③④可以任意颠倒~
2024-04-30 21:11:36
907
2
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人