自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+

清欢Allen的技术博客

When all else is lost the future still remains

  • 博客(79)
  • 收藏
  • 关注

原创 Linux 多线程( 进程VS线程 | 线程控制 )

为了对这些属性数据进行管理,OS采用了“先描述,再组织”的方式,该动态库中包含了一个个struct pthread结构体,其中包含了线程栈,上下文等数据,而线程ID(tid)便是动态库中每一个struct pthread结构体的首地址,进而CPU通过tid来找到对应的线程。joinable和分离是冲突的,一个线程不能既是joinable又是分离的,并且在常规线程分离的场景中,主线程一般用来创建新线程处理任务和回收资源,一般都是最后退出的。我们知道,全局变量,已初始化数据,未初始化数据等都是线程间共享的。

2023-09-16 20:52:34 1347 36

原创 Linux 多线程 ( 多线程概念 )

我们在以前所学习的进程知识中,一个进程由进程控制块(task_struct),进程地址空间( mm_struct ) ,页表,页表与进程地址空间,物理内存的映射为关系构成。每一个进程都有自己独有的进程地址空间和页表,对应的映射关系,所以我们在创建进程时需要耗费大量的时间,空间。但是,对于Linux系统,如果我们在创建一个进程后只创建task_struct,并且这些task_struct共享进程地址空间,页表等相关资源,图示如下。

2023-09-13 16:50:47 674 25

原创 Linux进程信号

信号是进程之间事件异步通知的一种方式,属于软中断。我们输入命令,在Shell下启动一个进程迎来循环打印一个字符串。sleep(1);return 0;我们可以使用kill -2 命令终止该进程。我们可以通过kill -l命令查看linux中定义的信号列表,其中,1 - 31号信号为普通信号,34 - 64号信号为实时信号。当然,我们可以使用 man 7 signal 查看各个信号的默认处理行为。

2023-08-31 11:58:50 2062 47

原创 Linux 进程间通信(IPC)

进程间通信(ipc), 指不同进程之间的信息传输或交换。

2023-08-22 16:34:47 629 2

原创 Linux中的动态库与静态库

我们在gcc 使用 -I -L 表明动态库的路径时,是对gcc编译器说的,当我们要运行加载程序的时候,此时已经生成了可执行程序,跟gcc没有任何关系,但是要运行时,进程需要通过->虚拟地址空间->页表映射去寻找动态库的位置,所以我们要对Linux系统表明动态库的位置.,静态库不需要将库的地址告诉操作系统是因为静态库在加载的时候已经将代码加载到内存中了,不需要再去与操作系统建立映射关系查找.与动态库的使用不同的是,我们使用gcc -I -L -l编译链接产生的可执行程序不可以直接运行.

2023-07-18 17:46:38 401 18

原创 C++ IO流

在C++中,我们可以使用stringstream来将整型变量的数据达转换为字符串形式.主要有两种使用方式:1.将数值数据格式转化为字符串.//方法一: int a = 10;string sa;s > sa;//从s中抽取前面插入的int类型的值,赋值给string类型 cout

2023-07-14 18:25:41 329 20

原创 C++ 类型转换

p的值修改后打印出来是一样的.但是,由于编译器会将const修饰的变量a加载到寄存器中,打印的时候编译器便会直接从寄存器读.所以,读取a时便还是以前的值,而。对于菱形继承来说,切片会产生偏移量,原本父类指针ptr1,ptr2应该都指子类bb,但是经过切片后,ptr1指向子类中的A1,ptr2指向子类中的A2.因为在Func函数中,形参为父类指针接收,可是,我们并不知道实参传的是指向父类的指针,还是指向子类的指针.无论采用c语言的强制转换,还是C++中的dynamic_cast强制转换.

2023-07-04 18:47:34 508 14

原创 C++ 特殊类设计

在C++98中,将该类的构造函数设置为私有,因为如果派生类继承了基类的私有成员,就意味着必须调用基类的构造函数对基类的私有成员初始化,但是基类的构造函数设置为私有,就意味派生类无法访问到基类的构造函数,所以该类被继承后也就无法创建出对象.一个程序中,多个单例,并且有先后创建初始化顺序要求时.饿汉模式无法控制,因为静态成员谁先初始化谁后初始化不能确定.比如,程序两个单例类A和B,假设要求A先创建初始化,B再创建初始化.所以,针对于单例对象,我们常用了内嵌类的形式,让程序结束时符合相关条件自动销毁.

2023-07-02 17:14:34 425 27

原创 C++ 智能指针

此时n1(智能指针)指向第一个结点,n2指向后一个结点,但是结点中的_prev,_next由原先的(shared_ptr转变为weak_ptr),而又因为weak_ptr不参与资源的管理,不决定结点的销毁,所以这时就不会造成两个结点之间的销毁由对方结点是否先销毁而决定的情况,由于结点2比结点1先创建,在函数栈帧销毁的过程中,结点2比结点1先销毁.可是如果n1中的_next指向结点2,n2中的_prev指向结点1时,当函数栈帧结束的时候,n2先析构,n1后析构,引用计数变为1.

2023-06-19 11:23:57 741 36

原创 C++ 异常处理

实际使用中很多公司都会自定义自己的异常体系进行规范的异常管理,因为一个项目中如果大家随意抛异常,那么外层的调用者就要写很多种catch来匹配对应的异常.所以在实际中都会定义一套继承的规范体.这样大家抛出的都是继承的派生类对象,捕获一个基类就可以了.此时,该catch中,异常对象e使用的虚函数为派生类中重写的虚函数.(构成多态).例如以下代码中,写了一个异常类Exception(基类),以及一个HttpServerException类(派生类).在抛异常时抛出的对象为派生类,捕获的类型为基类.

2023-06-06 16:43:15 892 32

原创 C++11 -- 包装器

/ 原型如下: template < class Fn , class . . . Args > /* unspecified */ bind(Fn && fn , Args && . . . args);注意( 1 ): fn 指的是需要包装的对象.( 2 ): args…对应的是给定fn函数中的参数.( 1 ) callable: 需要包装的对象…( 2 ) newCallable: 生成的一个新的可调用对象.

2023-05-30 23:25:29 836 20

原创 C++11 -- lambda表达式

在C++11后,我们便可以通过lamada表达式来解决,lamada表达式实际上就是一个匿名函数,这样我们即可通过lamada表达式直接了解sort排序的比较方式,进而提高了代码的可读性.lambda表达式语法lamada表达式的书写格式:lamabda达式各部分说明[capture-list]: 捕捉列表,该列表总是出现在lambda表达式的开始位置,编译器会根据[]来判断接下来的代码是否为lambda表达式, 捕捉列表能够根据上下文中的变量让lambda表达式使用.(parameters):

2023-05-28 17:30:21 859 19

原创 C++11 -- 可变参数模板

以下就是一个基本可变参数的函数模板.//Args是一个模板参数包,args是一个函数形参参数包. //声明一个函数形参参数包Args...args,其中这个形参参数包中可以包含0到任意个模板参数. template < class . . . Args > void ShouList(Args . . . args) {注意上面的参数args前面有省略号,所以它就是一个可变模板参数,我们将带省略号的参数称为"参数包",它里面包含了0到N个(N>>0)个模板参数.

2023-05-25 23:37:55 1710 21

原创 C++11 -- 类的新功能

如果想要限制某些默认函数的生成,在C++98中,是将该函数设置成private,并且只声明补丁而已,这样他人调用时就会报错,在C++11中更简单了,只需要在函数声明处加上=delete即可,该语法指示编译器不再生成对应函数的默认版本.我们了解,如果当我们显示写了拷贝构造,编译器就不会默认生成移动构造,可是如果我们在移动构造声明处加上关键字default,编译器就可以默认生成了.例如: 当我们提供了拷贝构造,就不会生成移动构造了,那么我们可以使用default关键字指定移动构造生成.

2023-05-23 11:44:21 693 31

原创 C++11 -- 右值引用和移动语义

( 1 ) : 左值引用只能引用左值,不能引用右值.( 2 ): 但是const左值引用既可以引用左值,也可以引用右值.

2023-05-12 10:16:12 507 22

原创 C++11 -- 入门基础知识

在2003年C++标准委员会曾经提交了一份技术勘误表(简称TC1),使得C++03这个名字已经取代了C++98称为C++11之前的最新C++标准名称。不过由于C++03(TC1)主要是对C++98标准中的漏洞进行修复,语言的核心部分则没有改动,因此人们习惯性的把两个标准合并称为C++98/03标准。从C++0x到C++11,C++标准10年磨一剑,第二个真正意义上的标准珊珊来迟。

2023-05-06 16:04:42 462 34

原创 哈希的应用 -- 布隆过滤器与海量数据处理

我们可以将么一个字符串多映射几个位(调用不同的哈希算法让同一个字符串映射不同位置),理论而言,一个字符串映射的位越多,则误判效率越低,但是也不能映射太多位置,因为映射的位置数越多,消耗的空间就越大,进而会导致布隆过滤器的优势降低.2: 我们要保证删除该元素后不会影响到其他元素,所以我们可以在位图的每一个比特位中设置一个计数值,如果铀元素插入到对应的比特位,那么该比特位的计数器就++,在删除时,我们只需要将该元素对应比特位计数器–就行.

2023-05-01 00:07:59 1235 45

原创 C++STL详解(十一)-- 位图(bitset)

有一道面试题:给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中?内存内查找: 面对40亿个无符号整数,我们可以使用搜索树和哈希表,时间复杂度也就为O(1),因为搜索树不仅存储数据,还要存储颜色,parent,child指针等,哈希表还要存储迭代器,size等内置成员,进而导致内存存不下.文件内查找:排序 + 二分查找,时间复杂度为0(1),但是数据太大,只能放在文件上,但是磁盘运行效率太低,不好支持二分查找).综合以上情况,我们可以采取位图解决。

2023-04-28 10:44:46 1908 25

原创 C++STL详解(十) -- 使用哈希表封装unordered_set和unordered_map

(1):set模板参数要求能够支持小于比较,例如:二叉搜索树查找成员函数中,我们可以利用compare仿函数将当前结点值与所给值比较,从而决定cur遍历左子树还是右子树,为了能够支持大于比较的,我们可以交换一下实参位置,这也间接支持了大于比较.并且条件判断,进而也支持了等于.(1)针对于K类型(指针,打浮点数,有符号整型)可以转换为无符号整数取模.对于string,日期类类型,这两个类型与整型类型无关的,此时不可以直接强转为整数,需要相应的提供将该类型转换为整数的仿函数.

2023-04-25 11:41:53 735 40

原创 哈希表(底层结构剖析--下)

方法一调用了Insert函数这样虽然可以解决创建新的哈希表导致每个数据的位置打乱,导致要重新计算插入位置的问题.可是,通过insert复用的办法,是生成了新的结点插入,函数栈帧结束后,旧表的数据结点通过调用析构函数销毁,可是,这样就会造成老的结点没有被充分运用的问题.一般来讲,因为内存限制,我们不太可能需要一个最大素数大小的哈希表,但是有可能我们传的值会比最大素数还大.但是,从语法上考虑,当这种情况发生,我们应该也要返回一个值,这里返回素数数组中最大素数.

2023-04-24 14:42:38 574 15

原创 哈希表(底层结构剖析-- 上)

1: 在顺序结构以及平衡树中,元素关键码与其存储位置之间没有对应的关系.因此,在查找一个元素时,必须要经过关键码的多次比较.我们知道顺序表查找的时间复杂度为0(N),平衡树中的查找的时间复杂度则为树的高度,即O(log2N),此时,搜索的效率取决于搜索过程中元素的比较次数.在以下数据集合中,11,21,31与1发生哈希冲突,只能在1的后续空位置中插入元素,但是11占用了2的位置,又导致11与2之间发生哈希冲突,而2又是占用了其他元素的位置,有可能引发别的元素之间的哈希冲突.

2023-04-23 12:08:02 905 27

原创 C++map/set与unordered系列的区别

1: 当我们处理数据数量较小的数据时,map/set与unordered系列在增删查改的效率差距不大.2: 但我们处理数据量较大的数据时,unordered系列在增删查改的效率比set/map的效率更高,性能更强.3: 除非我们需要在遍历容器时数据是有序的一般采用map/set,否则一般采用unordered系列容器.

2023-04-19 16:28:53 711 34

原创 C++STL详解(九)--使用红黑树封装实现set和map

如果我们用一棵KV模型的红黑树同时实现map和set,我们就需要控制map和set中的所传入红黑树中的模板参数,其中,封装过程中,为了与红黑树的模板参数区分,我们将红黑树中模板参数由V改成T.在插入中,结点中存储的类型T可能为Set容器中的key,也有可能是Map中的pair键值对,底层红黑树如何针对不同类型的容器进行比较呢?所以,当容器为set时,红黑树中的仿函数就实例化为set容器中的仿函数,当容器为map时,红黑树中的仿函数就实例化为map中的仿函数.正向迭代器还包括了==,!

2023-04-19 14:29:10 615 1

原创 红黑树(C++实现)

我们实现的是KV模型结构的红黑树,由于红黑树需要旋转,我们将红黑树的结点定义为三叉链结构,并且添加一个新成员_col,代表结点的颜色,方便后续的变色平衡.template < class K , class V > struct RBTreeNode //三叉链 {//存储的键值对 Colour _col;

2023-04-17 15:21:54 782 12

原创 AVL树(C++实现)

我们实现的是KV模型的AVL树,为了方便后续的操作,这里AVL树结点定义为三叉链结构,并且引入平衡因子,方便判断每棵树的平衡情况,并且还需要一个初始化列表将结点成员全部初始化.//三叉链 AVLTreeNode < K , V > * _left;//存储键值对 int _bf;

2023-04-14 20:54:44 1123 13

原创 C++多态

多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为.在继承中要构成多态还有两个条件:1: 必须通过基类的指针或者引用调用虚函数.2: 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写.

2023-04-12 15:50:13 121 2

原创 Leetcode:144.二叉树的前序遍历,94.二叉树的中序遍历,145.二叉树的后序遍历(非递归实现)

1:二叉树的前序遍历非递归实现我们可以按照访问左路结点–>左路结点的右子树顺序访问.就是遇到一个结点就将这个结点入栈,并将该节点放入到vector中.,当cur退出循环的时候,就说明对于这棵树来说根和右子树已经被访问完了.1:中序遍历的顺序为左子树–>右子树–>根,将遍历到的所有结点都入栈,但是不访问.,当cur退出第一次循环的时候,就说明该树的左子树已经访问了,此时我们可以访问根,即将放入vector,然后访问右子树.输入:root = [1,null,2,3]输入:root = [1,null,2]

2023-04-05 17:18:07 337 2

原创 Leetcode:106.从中序与后序序列遍历构造二叉树

1:从中序与后序序列遍历构造二叉树与中序与前序序列遍历构造二叉树不同的是,前序遍历的顺序遵循根–>左子树–>右子树,但是后序递归。输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]的顺序为左子树–>右子树–>根.所以根据后序遍历,我们应该按照根–>右子树–>左子树的顺序创建树.2:通过这个根确定左右区间,左子树的根一定在左区间,右子树的根一定在右区间.输出:[3,9,20,null,null,15,7]2: 遍历后序数数组时应该从后往前遍历创建根.

2023-04-03 18:48:25 424 11

原创 Leetcode105.从前序遍历与中序遍历构造二叉树

输入: preorder = [3,9,20,15,7], inorder = [9,3,15,20,7]4:当左右子树创建完毕后,此时便会回溯到第一个函数栈帧中右子树递归完毕的位置,此时我们要返回根结点.1: 用前序数组去创建根,然后遍历中序数组,找到与根创建相同的结点,并记录这个结点的下标.2: 通过这个下标就可以找出左右区间,左子树的根一定在左区间,右子树的根一定在右区间.输入: preorder = [-1], inorder = [-1]输出: [3,9,20,null,null,15,7]

2023-04-03 17:24:48 124 1

原创 Niuke:JZ36.二叉树与双向链表

1: 通过中序遍历,让先递归的结点在后递归的前面,每次左递归之后将prev与当前结点root的左指针指向上一层函数栈帧递归的结点,让上一层函数栈帧树结点的右指针指向当前结点,故而在每一层递归时应该保存当前结点的。输入: {5,4,#,3,#,2,#,1} 复制 返回值: From left to right are:1,2,3,4,5;输入: {10,6,14,4,8,12,16} 复制 返回值: From left to right。双向链表的其中一个头节点。

2023-04-02 09:33:20 317 2

原创 Leetcode: 236.二叉树的最近公共祖先

5:如果递归到一棵树的左右子树都为空,并且依旧还没找到,就说明这个结点root绝对不是目标结点的公共路径,所以要将这个结点出栈.并返回false.b : 当得到两个结点的祖先路径,我们要将保存两个结点中祖先路径较长的栈出栈,直到和另一个栈的路径长度相等.( 出栈的结点一定没有公共祖先).输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1。输入:root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4。

2023-04-01 18:32:46 974 2

原创 二叉搜索树(二叉树进阶)

如果min在minParent的右边,及删除结点右子树的最左结点为空的情况下,此时min就是右子树的最左结点,如果minParent初始化为nullptr,则无法进入循环寻找右子树的最左节点,所以可以将minParent初始化为cur.然后将minParent的右指针指向min的右孩子后将cur与min的值替换删除.(a):寻找右子树的最左结点.用cur记录要删除结点,根据右子树的最左结点必为空的特性下不断向左循环查找,并且我们要记录右子树最左节点的父节点,便于父节点链接最左节点的孩子结点.。

2023-03-30 17:53:50 431 6

原创 C++STL详解(八)-- set,map,multiset,multimap的介绍与使用

1: 与map/multimap不同,map/multimap存放的是真正的键值对,set中只放value,但在底层实际上存放的是中所构成的键值对.2: set中插入元素时,只需要插入value即可,不需要构造键值对.3 set中的元素不可重复(因此可以去重),因为set底层是搜索二叉树,搜索二叉树的特征便是升序+去重.4: set构造函数中的伪函数默认是less,代表的是升序,如果我们想用降序,可以采用great.

2023-03-27 15:21:58 696 12

原创 C++模板(进阶)

可是,push_back函数模板声明与定义是分开的,导致在编译阶段就无法确定T的参数类型,也就说明只有函数声明没有函数定义了,那么函数地址只能从链接阶段去找,可是,没有函数定义,也就无法将函数地址放进符号表中,编译器无法在符号表中根据函数声明去寻找对应的函数地址了.p1指向的对象明显小于p2指向的对象,但是在Less内部中,并没有将p1指向的对象与p2指向的对象相比较,而是单纯的比较的是p1,p2指针的地址,而p1,p2的地址根据函数栈帧由高到低排布的,所以p1的地址比p2大,进而导致错误的结果.

2023-03-22 10:08:31 413 2

原创 C++继承

1: 继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序:员在保持原有类特性的基础上进行扩展,增加功能,这样产生的类,称为派生类。2: 继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。继承为是类设计层次的复用。public ://继承后父类的Person成员(成员函数+成员变量)都会成为子类的一部分,这里体现出Stuident复用Person的成员。is-a关系。

2023-03-20 11:26:21 477 3

原创 C++STL详解(七)——priority_queue的使用和模拟实现

优先级队列默认使用vector作为底层的存储的容器,在vector上又使用了堆算法将vector中元素构造成堆的结构,因此priority_queue就是堆,所有需要用到堆的位置,都可以考虑使用priorityda_queue。注意默认情况下priority_queue是大堆。方式一使用的是vector作为底层存储容器,内部为构造为大堆结构。//大堆 priority_queue < int , vector < int > , less < int >> q1;方式二。

2023-03-14 20:58:38 392 6

原创 Leetcode:150.逆波兰表达式求值,155.最小栈 牛客:JZ31.栈的压入,打弹出序列

例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。由于是[1,2,3,4,5]的压入顺序,[4,3,5,1,2]的弹出顺序,要求4,3,5必须在1,2前压入,且1,2不能弹出,但是这样压入的顺序,1又不能在2之前弹出,所以无法形成的,返回false。:如果正常栈的栈顶和最小的相同,辅助栈出栈,栈出栈.否则的话,辅助栈不出栈,栈正常出栈。输入:tokens = [“2”,“1”,“+”,“3”,“*”]

2023-03-12 15:34:24 279 2

原创 C++STL详解(六)——stack和queue

方式一:使用STL中默认的适配器定义栈。(默认为deque)方式二:使用特定的适配器定义栈定义方式一:使用STL中默认的适配器定义队列。(默认为deque)定义方式二:使用特定的适配器定义队列。

2023-03-11 20:11:01 1009 3

原创 C++STL详解(五)——list的介绍与使用

1:list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。2:与其他序列容器相比(array,vector,deque),list通常可以在任意位置进行插入,移除等效率更高。

2023-03-08 20:38:31 1392 8

原创 Leetcode:17.电话号码的字母组合,118.杨辉三角,136只出现一次的数字

2:对于Combine函数,我们要传digits映射对应下标的字符串,需要变量di(不能用引用,因为回溯时是回到上一层,而此时的di已经为上一层的di了)记录字符串的串数(抽象看也叫二叉树的层数从0开始),然后要将存放结果的vector传过去,并且我们还需要String combineStr 来存储对应数字下标的字符串,并且还需要利用combineStr的返回值进行组合字符串。输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]]输入:nums = [2,2,1]

2023-03-04 01:01:21 384 1

空空如也

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除