- 博客(17)
- 收藏
- 关注
原创 用哈希表封装myunordered_map和myunordered_set
• 另外,由于迭代器需要哈希表的指针来访问表,因此需要将迭代器设置为哈希表的友元类。这里的难点是反而是结构设计的问题,所以iterator中除了有结点的指针,还有哈希表对象的指针,这样当前桶走完了,要计算下一个桶就相对容易多了,用key值计算出当前桶位置,依次往后找下一个不为空的桶即可。对于unordered_map,我们还需要重载[],unordered_map的[]有两种情况,如果key不存在,就插入,并返回插入后value的引用,如果key存在,则插入失败,充当查找功能,返回value的引用。
2025-08-09 11:46:48
461
原创 哈希表实现
当key是string/Date等类型时,key不能取模,那么我们需要给HashTable增加一个仿函数,这个仿函数支持把key转换成⼀个可以取模的整形,如果key可以转换为整形并且不容易冲突,那么这个仿函数就用默认参数即可,如果这个Key不能转换为整形,我们就需要自己实现一个仿函数传给这个参数,实现这个仿函数的要求就是尽量key的每值都参与到计算中,让不同的key转换出的整形值不同。⼀般情况下,不断扩容,单个桶很长的场景还是比较少的,下面我们实现就不搞这么复杂了,这个解决极端场景的思路,大家了解一下。
2025-08-08 21:06:32
920
原创 unordered_map和unordered_set的使用
• unordered_set和set的第一个差异是对key的要求不同,set要求Key支持小于比较,而unordered_set要求Key支持转成整形且支持等于比较,要理解unordered_set的这个两点要求得。• unordered_map和map的第二个差异是迭代器的差异,map的iterator是双向迭代器,unordered_map是单向迭代器,其次map底层是红黑树,红黑树是二叉搜索树,走中序遍历是有。而unordered_set底层是哈希表,迭代器遍历是无序+去重。
2025-08-08 11:33:41
262
原创 封装map和set
如果该节点是父亲节点的右孩子,这个节点所在子树已经遍历完了,而该子树有充当着父亲节点的右子树,说明父亲节点所在字数也遍历完成,那就需要继续向上回溯,直到当前节点是父亲的左孩子,说明父亲节点的左子树遍历完成,按照中序遍历“左->根->右”的顺序,下一步就要遍历父亲节点了,因此回溯结束,下一个节点就是该父亲节点。对于map,我们还需要重载map的[],map的[]有两种情况,如果key不存在,就插入,并返回插入后value的引用,如果key存在,则插入失败,充当查找功能,返回value的引用。
2025-08-08 11:18:02
603
原创 红黑树的实现
2.2.2 情况1:变⾊c为红,p为红,g为⿊,u存在且为红,则将p和u变⿊,g变红。分析:因为p和u都是红⾊,g是⿊⾊,把p和u变⿊,左边⼦树路径各增加⼀个⿊⾊结点,g再变红,相当于保持g所在⼦树的⿊⾊结点的数量不变,同时解决了c和p连续红⾊结点的问题,需要继续往上更新是因为,g是红⾊,如果g的⽗亲还是红⾊,那么就还需要继续处理;红⿊树通过4条规则的颜⾊约束,间接的实现了近似平衡,他们效率都是同⼀档次,但是相对⽽⾔,插⼊相同数量的结点,红⿊树的旋转次数是更少的,因为他对平衡的控制没那么严格。
2025-05-29 21:06:54
838
原创 AVL树的性质和实现
• 场景1:h>=1时,新增结点插⼊在e⼦树,e⼦树⾼度从h-1变为h并不断更新12->15->10平衡因⼦,引发旋转,其中12的平衡因⼦为-1,旋转后10和12平衡因⼦为0,15平衡因⼦为1。• 场景2:h>=1时,新增结点插⼊在f⼦树,f⼦树⾼度从h-1变为h并不断更新12->15->10平衡因⼦,引发旋转,其中12的平衡因⼦为1,旋转后15和12平衡因⼦为0,10平衡因⼦为-1。• 旋转核⼼步骤,因为5<b⼦树的值<10,将b变成10的左⼦树,10变成5的右⼦树,5变成这棵树新。
2025-05-28 19:03:01
726
原创 多态的使用
注意:在重写基类虚函数时,派⽣类的虚函数在不加virtual关键字时,虽然也可以构成重写(因为继承后基类的虚函数被继承下来了在派⽣类依旧保持虚函数属性),但是该种写法不是很规范,不建议这样使用,不过在考试选择题中,经常会故意买这个坑,让你判断是否构成多态。基类的析构函数为虚函数,此时派⽣类析构函数只要定义,⽆论是否加virtual关键字,都与基类的析构函数构成重写,虽然基类与派⽣类析构函数名字不同看起来不符合重写的规则,实际上编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统⼀处理成。
2025-05-16 20:45:55
632
原创 String类
在insert函数中,进行后移时,我们的语句是_str[end+len] = _str[end],这里是通过数组下标引用进行改变,而不是通过string对象的下标进行引用改变,因此这里的end+len只要小于_capacity即可,而通过string对象下标引用需要小于_size。写时拷贝和引用计数是一种博弈的心理,如果我们只读不写,就可以通过引用计数的浅拷贝来减少深拷贝的开销,如果需要进行修改还是需要进行深拷贝。资源,就给计数增加1,当某个对象被销毁时,先给该计数减1,然后再检查是否需要释放资源,
2024-10-17 10:01:00
1219
原创 STL简介
由P. J. Plauger开发,继承自HP版本,被Windows Visual C++采用,不能公开或修改,缺陷:可读性比较低,符号命名比较怪异。STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的。由Rouge Wage公司开发,继承自HP版本,被C+ + Builder 采用,不能公开或修改,可读性一。移植性好,可公开、修改甚至贩卖,从命名风格和编程 风格上看,阅读性非常高。STL要阅读部分源代码,主要参考的就是这个版本。
2024-10-06 01:00:43
311
原创 模板-初阶
模板函数的调用过程是,实参传给模板函数里的形参,模板函数推出具体的类型和函数,再进行函数调用,模板函数是不可以直接调用的。在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。1. 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增。函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生。类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。可以看到,调用时会调用具体的Add函数。
2024-10-06 00:27:28
993
原创 C/C++内存管理
在绝大部分场景下,我们不会用这样的方法去申请空间,但在一些特殊的情况下不得不使用。比如在编程中有一个概念叫做池化技术,池化技术中有一个叫做内存池,对于一些高频申请和释放内存块的进程,系统会给其设置一个内存池,内存池中的内存给这个进程专用,而如果我们向这个内存池中申请空间,就不能用new向堆上申请空间了,这时候就要用到operator new申请空间,并用定位new来显示调用构造函数初始化。new/delete申请和释放的是单个元素的空间,new[]和delete[]申请的是连续空间,而且new在申。
2024-10-05 15:54:06
906
原创 类和对象(下)
我们再创建aa2的时候,用了另一种方式,这时候,2是int类型的,而aa2是A类型的,这时候就会发生隐式类型转换,本来的转换是2默认构造一个类型为A的临时变量,再调用拷贝构造拷贝给aa2,但是c++优化了这一点,将这种连续构造+拷贝构造优化为直接构造,得益于A有单参数的构造函数。所有的成员变量都会走初始化列表,const、引用和没有没有默认构造的自定义类型也不例外,因此在声明的时候给const、引用和没有没有默认构造的自定义类型缺省值,不在初始化列表显示初始化也能编译通过,这其实是C++11的一个补丁。
2024-09-19 11:44:32
875
原创 类和对象(中)
=的逻辑是,比较_day和day的大小,如果不够,我们就将上一个月的天数GetMonthDay加在_day上,_month--,再进行比较,直到_day>day时,循环结束。对于第七点,上图可以很好地解释,对于MyQueue这个类,我们没有写他的构造函数,则系统会自己生成,而系统自己生成的默认构造函数,对于自定义类型,要求调用这个成员变量的默认构造函数初始化,所以就调用了Stack的默认构造,而Stack里面我们写了一个全缺省构造函数,那么mq就被成功初始化了,pushst和popst也都被初始化了。
2024-09-16 02:17:23
591
原创 类和对象(上)
再分析一下,对象中是否有存储指针的必要呢,Date实例化d1和d2两个对象,d1和d2都有各自独立的成员变量_year/_month/_day存储各自的数据,但是d1和d2的成员函数Init/Print指针却是⼀样的,存储在对象中就浪费了。这里需要再额外哆嗦⼀下,如果处理器可以从任意位置开始访问,那么无疑第一种会节省空间,但是问题就出在处理器不能从任意位置开始访问,比如从4个字节的整数倍位置开始访问,那么第一种存储方式想访问int只能从0位置开始访问,并且一次访问无法完成,需要访问两次,增加了时间的消耗。
2024-09-09 11:19:38
850
原创 C++入门基础
再者,我们之前学习的顺序表,在尾插顺序表的时候,我们需要定义一个单个节点的指针,也就是一个结构体的指针,在尾插接口中,我们想要改变这个结构体的指针,就要传结构体指针的地址,并且用结构体的二级指针接收,这样才能通过形参改变外面的实参。这里是c++的IO流,其中的<<在这里是"流插入”,endl是换行,是end line的缩写。定义,在使用空值的指针时,都不可避免的会遇到⼀些麻烦,本想通过f(NULL)调用指针版本的f(int*)函数,但是由于NULL被定义成0,调用了f(int x),因此与程序的初衷相悖。
2024-09-06 22:37:25
1074
原创 数据结构中的各种排序算法
前言:在学习排序之前,我们先了解一下排序的一些概念和排序是会用到的交换算法。(1)排序:所谓排序,就是使一串记录,按照其中的某个或某些关键字的大小,递增或递减的排列起来的操作。(2)稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。(3)交换算法:交换数组中的两个数据,我们一定是传数据的地址,
2024-09-05 19:09:02
630
2
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人