- 博客(72)
- 收藏
- 关注
原创 红黑树详解
红黑树,是一种二叉搜索树,但添加了 '红' 和 '黑' 两种颜色标记位。红黑树的最长路径最短是最短路径的二倍,它不像 AVL树一样,它不是严格的平衡树。
2025-01-08 10:44:31
898
6
原创 C++跳表的实现
跳表(skiplist)和哈希表、平衡搜索树一样,也是一种查找结构。跳表多使用在 kv 型数据库中, Redis 中就会使用跳表平常有序的单链表,查找时间复杂度为O(N)。我们该如何考虑优化它呢?跳表中的思想就是将中间节点升高,直接和高层节点比较,大于它就向后,小于就向左,提高了查找效率。如果每一层的上层是下层的一半,那么就等同于二分查找。但是在插入和删除的过程中很难维护这个条件(因此,跳表选择了随机层数,每一个节点的层数都是随机的。
2024-12-21 11:05:41
923
1
原创 C++智能指针详解
智能指针是一个类似于指针的类,将指针交给这个类对象进行管理,我们就可以像使用指针一样使用这个类,并且它会自动释放资源。智能指针运用了 RAII 的思想(资源获得即初始化)。RAII 是指,用对象的生命周期来管理资源,类对象创建时拿到资源,析构时释放资源。1、不需要显式释放资源。2、在对象生命周期内,资源始终都是有效的。在上述代码中,指针 p1, p2, p3 在创建时都有可能出现异常,如果在 p1 创建时出现异常,那么我们只需要捕获;
2024-12-17 20:27:36
658
原创 特殊类的设计
要设计一个不能被拷贝的类有两种方式:1、将拷贝构造和赋值重载设为私有2、用 delete 关键字,将拷贝构造和赋值重装设为删除状态。
2024-12-11 15:30:00
875
原创 哈希知识详解
以前,在面对海量数据的查找时,最快就是红黑树 logN,无法满足需求。于是探索出了另一种用关键字 key 值与其存储位置建立映射的思想,让查找效率提升到 O(1) ,这个就是哈希。
2024-12-08 21:13:18
1104
原创 C++右值引用
右值指的是不能出现在等号左边不能被赋值不能取地址的值。右值引用就是右值的引用,给右值取别名。1、语法上,引用都是取别名,不开空间,左值引用给左值取别名。右值引用就是给右值取别名。2、底层,引用都是用指针实现的。左值引用存的是当前左值的地址。右值引用是把右值拷贝到栈上的一个临时空间,再存放这个临时空间的地址。3、非const 左值引用不能给右值取别名,但const 左值引用可以。4、 右值引用不能给左值取别名,但是左值 move 后,可以。
2024-11-02 20:44:19
1648
1
原创 C++多态
多态就是完成某种行为时,当不同的对象去做就会产生出不同的状态。比如买票:普通成年人 和 学生 进行买票时,学生有优惠,而 普通成年人 没有。
2024-10-27 11:49:51
713
2
原创 C++继承
1、当以 public 方式继承时,基类中的 public 成员还是派生类的 public 成员,protected 成员还是 protected 成员,private 在派生类中不可见。(以 public 方式继承时,与其在基类中限定符一样,除 private 是不可见)2、当以 protected 方式继承时,基类的 public 和 protected 成员在派生类中都是 protected 成员,基类中的 private 成员依旧是不可见。
2024-10-24 20:31:51
1116
4
原创 并查集的简单实现
如果我们要将上面的图中的 3 和 7 合并,那么我们就需要先找到 3 的根和 7 的根,再将它们的根连接起来即可。我们只需要先判断当前节点是否为根,是就返回,不是就让它去判断它父亲是不是根,再没找到,就依次向上寻找即可。初始状态下,所有下标对应的值为 -1,表示每个元素自身为一个集合,集合内的个数为 1。例如:上图中的 5 和 7,它们找根时发现根相同,那就不需要合并,直接返回即可。我们还要考虑到,如果要合并的两个元素本身就在一个集合中,那么我们就不需要合并了。
2024-05-18 08:47:59
429
2
原创 信号知识详解
信号是linux系统提供的,让用户或进程给其他进程发送异步信息的一种方式。常见的信号处理方式:1、默认行为2、忽略3、自定义我们可以使用命令 kill -l 查看信号,也可以使用 kill -signum pid 对指定进程发送信号。例如:用 kill -9 将指定的死循环进程杀死。同样,我们也可以用 kill 命令发送其他信号。查看信号可以用 man 7 查看 man 手册。先介绍一个系统调用:signal(),它可以改变信号的默认行为,变为自定义。
2024-05-02 14:16:30
158
9
原创 文件描述符详解
在使用 open() 函数时,就是将磁盘中的文件加载到内存,创建一个 struct file 对象,然后将其属性内容等填好,然后链接到文件队列中,再返回新创建的 struct file 的地址,填到 fd_array[ ] 最小的,未被使用的下标中,再返回这个下标,就让我们拿到了文件描述符 fd。使用 read() 函数,就是先用 文件描述符 fd 找到对应的 struct file 文件对象,然后读取缓冲区的数据到 buf 中,如果缓冲区内没有数据,就将磁盘中的数据导入到缓冲区,再读。
2024-04-16 20:49:35
367
原创 命名管道详解
管道都是基于文件的。1、当写端未关闭,读快写慢,当文件内没数据时,读端就会在read阻塞等待,写端写了再读2、当读端未关闭,写快读慢,写端就会一直写,直到写满,阻塞等读端读,然后再写。3、当写端关闭,读端就会读到0,表示读到结束。4、当读端关闭,如果写端还在写,会通过13信号杀死进程。5、在使用open时,先打开读端或写端中的任意一个,open()函数都会阻塞,只有读端和写端都打开了,才能正常运行。
2024-04-12 10:23:30
385
原创 进程间通信 (匿名管道)
进程间通信是一个进程把自己的数据交给另一个进程,它可以帮助我们进行数据传输、资源共享、通知事件和进程控制。进程间通信的本质是让不同的进程看到同一份资源。因此,我们要有:1、交换数据的空间。2、这个空间不能由通信双方任意一方提供。(要有一个独立的空间)
2024-04-10 23:05:24
870
5
原创 进程替换详解
e代表环境变量,程序替换不会替换环境变量,我们可以通过带e的接口设置新的环境变量。我们可以创建子进程,然后把子进程替换成我们需要的进程,执行其他程序的代码。并且我们代码中的 exec end 没有打出来,验证了进程替换后,后续代码不再执行。进程替换后:1、程序替换后,替换函数之后的代码不会被执行,因为进程已经被替换了。3、替换完成,不创建新的程序。我们可以观察得到,这些函数前面都有exec,后面跟着的。进程替换就是把当前进程替换成我们需要执行的进程,一般是通过系统调用实现。4、程序替换不会替换环境变量。
2024-04-08 20:45:15
309
4
原创 动静态库详解
静态库是程序在编译链接时将库的代码链接到可执行文件中,生成的可执行文件不需要库也可以运行。静态库是以 .a 为结尾的。打包静态库的方法:1、假设我们有一系列的 .c 和 .h 文件,第一步就是把 .c 文件编译成 .o 文件,通过指令:gcc -c 【.c文件名】,就会生成同名 .o 文件,如图:2、通过命令:ar -rc libXXX.a 【你要打包的 .o 文件名】,就可以生成静态库。
2024-03-22 20:00:17
1010
3
原创 软硬链接详解
软链接就相当于Windows里的快捷方式,里面放的是目标文件的路径。用【 ln -s 目标文件名 软链接文件名 】可以 建立软链接。
2024-03-13 20:11:55
1495
3
原创 进程等待详解
options 表示是否进行阻塞等待,0表示阻塞等待,1表示非阻塞等待,也可以用宏,WNOHANG表示非阻塞等待,WUNTRACED表示阻塞等待。在非阻塞状态下,成功返回子进程pid,还在等返回0,失败返回 -1.僵尸进程的危害十分严重,为了防止僵尸进程,及时地回收资源,我们可以采用进程等待的方式。3、waitpid()的作用也是等待,但它可以更细致的控制。是等待任意一个子进程,默认为阻塞等待(等待时父进程阻塞,不能做其他事)。2、父进程通过等待,获取子进程的退出信息。成功,返回等待的子进程的pid;
2024-03-13 16:58:31
572
3
原创 进程地址空间
当我们运行如上代码时,出现了地址相同而存的值不同的情况,由此我们可以验证,我们拿到的地址都是虚拟地址,fork之后创建了新的子进程,而新的子进程给它分配了新的一份虚拟地址,他们在各自的虚拟地址上对应到不同物理地址,修改 i 的值,就出现了上述情况。而这里指的内存并不是真实的物理内存,而是虚拟内存,每一个进程都有对应的虚拟内存,再通过页表与物理内存建立对应的映射,从而完成虚拟内存到物理内存的转换,这些工作都是由操作系统完成的,在每一个进程看来,它们都拥有完整的一块内存。b、将进程管理与内存管理进行解耦合。
2024-01-23 10:15:07
396
原创 vector的模拟实现
我们可以得知,_finish - _start就是顺序表vector的大小;_end_of_storage - _start 就是顺序表vector的容量。
2024-01-20 16:16:19
1055
8
原创 系统调用创建子进程 --- fork基础认识
输入指令:man 2 fork 就可以看到fork的介绍( q 退出)fork()的参数为空,作用是:在调用fork()之后,创建一个子进程,子进程与父进程共享fork()之后的代码,数据各自私有一份,采用的是写时拷贝。返回值:父进程的返回值是子进程的pid,子进程的返回值是0。出错的话父进程返回-1,并且没有子进程被创建。运行结果第一行是父进程的,它的pid是31243,fork()的返回值是子进程的pid;第二行是子进程的,它的pid是41244,fork()的返回值是0。
2024-01-05 10:47:06
593
6
原创 string类的模拟实现
我们先用strcmp可以实现字符串的 operator== 以及 operator> 再复用这两个函数完成接下来的函数的实现。因为在类里面第一个参数默认是this,而我们的习惯用法是cin >> string 、cout
2023-11-27 11:14:35
722
2
原创 泛型编程 -- 模板详解
在没有模板之前,如果我们写一个swap()两数交换函数,因为我们要支持 int 与int 交换 、double 与 double 交换等等情况,所以要实现swap()函数的多个重载,显得很繁琐,于是就引入了模板。模板就是在需要模板的地方的上面加一行:template 或 template需要多个参数还可以加:template这里 T 就代表的是一个不具体的类型,在调用时编译器自动识别会实例化或者我们手动给它实例化。
2023-11-17 10:31:20
159
原创 makefile的基础使用
1、建一个目录: mkdir Makefile/makefile(两个任意一个就可以)2、用vim打开3、在makefile里面的写法:目标文件 : 依赖文件==>小例子:[tab]依赖关系4、.PHONY:伪目标,让 make 命令 可以多次调用用法:以clean为例5、$@ 表示所有目标文件, $^ 表示所有依赖文件。
2023-11-10 11:35:34
113
原创 详解静态成员变量以及静态成员函数
类的静态成员变量是该类的所有对象共有的(只有一份),只能在类里声明,类外定义。相当于只属于类的全局变量。1、定义: 只能在全局中定义2、访问方式:(假如类A 中有公有静态变量 _a) ,可以用 A::_a 或 A a;a._a;访问3、不支持给缺省值(因为静态成员变量不走初始化列表)4、静态成员变量只能初始化一次,所以不能走初始化列表5、计算类的大小时不包括静态成员变量的大小,因为它在静态区,不算在类里。
2023-11-08 20:45:06
687
3
原创 文件权限详解
ll指令查看文件详细信息中,第一列就是文件类型。常见的文件类型有:1、 - :普通文件 (文本、源代码、图片、视频、可执行)2、 d :目录文件3、b :块设备4、c :字符设备5、l:链接文件6、p:管道文件7、s:socket文件。
2023-10-27 20:46:17
2726
5
原创 C++类的默认成员函数
在我们用C语言实现栈或队列等数据结构时,我们通常要写一个函数 Init() 来初始化它们,每次使用前都要调用一次,在我们使用的过程中容易忘记初始化导致程序错误,于是在C++中有了类的构造函数来帮助我们初始化编译器会在创建类对象时自动调用构造函数完成初始化,十分方便。那么构造函数应该怎么定义呢?1、构造函数的名字与类名相同2、构造函数没有返回值3、构造函数可以写多个构成重载(就像有时我们只要一个空栈,有时需要用数组初始化而成的栈)4、如果我们不写构造函数,
2023-10-25 21:16:21
195
1
原创 C++入门之引用与内联函数
引用在语法上的理解就是起别名用法就是在类型后面加&,例子:int a = 1;int& b = a;上例所示,执行后,b就是a的别名,它们代表同一块空间,a的改变会影响b,b的改变也会影响a。
2023-10-17 15:08:25
904
11
原创 C++入门之缺省参数与函数重载
在写声明和定义分离的带缺省参数函数时,只有声明要给缺省参数。因为1、如果声明和定义都给的话万一给的不一样,冲突不好处理2、如果只在定义中给在头文件中就看不到了,不方便使用(C++支持,只包头文件就可以调用函数)只有声明中要写缺省参数若声明定义都给若只有定义给两者都会报错二、函数重载函数重载:C++中支持声明和定义同名函数(只要参数不同),这些同名函数就构成函数重载。这里的参数不同包括参数的类型不同,个数不同,顺序不同。
2023-10-13 20:19:49
285
4
原创 C++入门之命名空间详解
命名空间的功能就是区分不同的代码段,避免使用不同代码时带来变量名冲突的问题。在写C语言代码时,常常回面临命名冲突的问题。例如:可以成功运行。但是如果要使用 time.h 头文件时,就会与库发生冲突还有在包其他同学写的头文件时,也有会发生命名冲突的可能。为了解决这一问题,C++引入了命名空间,把自己写的代码放进命名空间中,就能解决这一问题。我们可以看到,上面显示重定义的错误在给我们自己定义的变量加上命名空间后就消失了程序运行后就打印出来了clock这个函数指针的值。
2023-10-11 18:47:04
382
4
原创 八大排序详解(默认升序)
直接插入排序的时间复杂度为O(n^2)空间复杂度为O(1)具有稳定性(相对位置不变),在接近升序的情况下效果最好,接近O(n)。
2023-10-07 19:35:02
517
6
原创 二叉树链式结构基础
所以在遍历过程中栈不会为空,栈为空就代表所有结点都遍历完成,结束循环。中序是先把左子树干掉,再把自己干掉,自己没了就找不到右孩子了,也不行。后序是先把左右子树干掉,在释放自己,刚好符合我们的要求,所以就用后续。层序遍历:和名字一样,就是一层一层遍历,如上图示例,层序遍历就是。前序遍历就是先保存根,然后向左探索,遇到空回来后向右探索。查找值为x的结点,遍历查找,前中后序都可以,找到返回即可。左右子树可以为空,就是单节点,根为空就表示探索完成,返回。先向左探索,等到为空返回时,再记录当前结点,最后向右探索。
2023-09-20 15:16:43
118
7
原创 堆排序与TopK问题
堆排序的思想就是先用数组模拟建大堆,然后把根结点与最后一个结点值交换,最后一个结点的值就是最大值,然后再把前(n-1)个元素重新建大堆,然后根结点与最后一个结点值交换,就找出了第二大的结点,....,重复操作就可以把数组排成有序。而建大堆可以找出最大的元素,再用最大的元素和最后一个元素换位,让最大的元素跑到最后面,再调用adjustdown(),调整建堆个数,让前n-1个数建堆,重复操作完成排序。:堆排序就是模拟建堆,然后找出最大的元素放最后一位,再找出次大的元素放倒数第二位,依次类推,最后变成有序。
2023-09-12 15:50:30
156
7
原创 栈和队列详解
队列:队列也是一种特殊线性表,只允许在一端进行插入,在另一端进行删除。栈:栈是一种特殊的线性表,它只能在一端进行插入和删除,插入和删除的一端叫做。检测栈是否为空,如果为空返回非零结果,如果不为空返回0。检测队列是否为空,如果为空返回非零结果,如果非空返回0。,因为它只能在一端进行插入和删除,所以它有着。,因为它是一端插入,另一端删除,所以它具有。获取队列中有效元素的个数。获取栈中有效元素个数。
2023-09-02 10:14:18
154
1
原创 牛客:数对
解题思路:看到题目的时候,一般第1反应是用两个循环暴力解题,时间复杂度是O(n^2),不能通过,所以要优化,通过找规律。式子:(n / y) * (y - k) +((n%y < k)?如果前式大于等于k,有n%y-(k-1)个可能。三、可以算出有 n/y 个完整的区间,所以有(n / y) * (y - k) 个可能。一、当 y <= k 时, 不可能符合题意,所以 y 从 k+1 开始遍历。二、当 x 属于区间 [0, y) 时,有 y-k 个可能的数对。
2023-08-24 23:06:31
136
原创 牛客:不用加减乘除做加法
大体思路:先不考虑进位用异或把数字“加”起来,在“加”上进位就可以了。题目所述不能用 + - * / 这四个运算符号,所以想到用位运算。
2023-08-22 20:51:28
67
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人