- 博客(81)
- 收藏
- 关注
原创 补码一位乘法原理(布斯编码详讲)
我们计算的时候,是根据上面的竖式来计算的,原理就不说了,乘数中每位分别乘被乘数,然后在相加,这样的话,我们又6个非0的部分积,也就是要加6次,但是,我们可以把01111110写成10000000-00000010,这样的话我们就可以把原式写为11010000*(10000000-00000010),探后根据乘法分配律,写为11010000*10000000-11010000*00000010,这样的话,由以前的6次部分积的相加变成了2次部分积的相加,提高了乘法的效率。那么,补码乘法的原理到底是什么呢?
2024-07-10 17:54:35 415 2
原创 网络之再谈体系结构
很简单,最直接的一个列子就是NAT技术,他是工作在网络层的,但是如果这样的话,他结合NAPT技术转换的时候,把我们的上层也就是传输层的端口号给改了,这说明什么,这说明下层我是知道你上一层的内容的(这里只是特殊处理,所以使得下层知道了上层的内容),如若不然,他是改不了我们传输层的端口号的,如若不用这用这个技术,那么好,你怎么用公网的资源,怎么访问公网服务器?所以,个人认为这里的ARP协议与这里有点像,如果我有了网络中的唯一标识主机的地址,但是,跨网怎么办?这个是个人理解,若是有大佬知道,希望可以说一下。
2024-05-27 00:12:14 662
原创 操作系统中的“上帝“,中断
所以现代操作系统应该是不会有用这个方法的。其次,就是进程调度,这个不用我说有多么的重要了吧,如果没了进程调度,那我们想想其实就很可怕,运行了一个应用程序之后,那我们只能等这个程序运行完,然后才开始下一个,在运行期间不管点什么都没有用,所以,就光说说就要不合理,所以,它内部不只是有进程调度算法,我个人觉的,没有中断,算法在完美,在高效,那也是没有用武之地啊。所以,OS中很多功能的完成其实都是用到了中断,如果没有中断,想想就可怕,所以说,我个人觉的,说其是OS的上帝一点都不为过。希望大家能够支持一下!
2024-05-05 14:48:18 295
原创 I/O系统调用,你未必懂吧!!
其实不知道大家有没有疑惑的就是,我们读取文件或者输入字符的时候,所用的库函数不一样,其实根本原因就是不同IO硬件的驱动程序不一样,也就是IO控制器不一样,例如IO控制中的寄存器不一样(打个比方,也有其他东西不一样)。很多人其实误认为IO设备其实仅仅只是一个键盘啊,显示器啊,网卡什么的,其实怎么说呢,这其实说的不完全正确,因为他还有一个,也就是说还有一个设备,组合起来才可以说是完整的IO设备,这个设备就是IO控制器,是的,IO控制器是一个电子设备,IO设备是电子设备加机械设备,只有这样才可以说为是IO设备。
2024-04-28 23:18:19 229
原创 线程,你真的懂了吗?
其实不然,我们都只的是,其实pthread.h这个其实就是一个类似于管理线程的线程库,里面有创建线程,回收线程等等功能的函数,且深入理解过linux下的线程,都知道的是其实没有所谓的线程,只有轻量级进程。而我们在学习操作系统的时候,都知道用户级线程是不能被阻塞的,如果阻塞,那么整个进程势必阻塞,所以,刚开始我以为linux下直接就是创建了一个轻量级进程,但是我仔细想了一下,不对,因为pthread这个是库,是在用户层面上封装的。很简单,目前的linux系统是1:1模式的,也就是说,内核中有了“内核进程”。
2024-03-20 00:29:38 466
原创 模拟实现B树的
这个一定要注意一下。第一种就是,不管此时的这个要提取中位数的节点是他父节点的左孩子还是右孩子,我们直接把中位数提取到父亲节点,也不需要排序,直接插入到父节点的最后一位置,然后此时重点来了,没错,大概是插入排序,是的,我们在这里用一个插入排序的思想,这里就会很方便。首先在这里说一下,B树其实与其他数据结构的实现有所不同,其他数据结构实现的时候,我们只需要按我们自己的思路,写下来,或许中间有几个bug,一改就好,但是B树是不同的,B树是先把大体思想的框架写出来,再根据往里插入的数据来修改bug。
2024-03-07 00:37:04 421
原创 B树系列(详解)
因为我们用B树的时候,B树的每个节点的所存储的关键字数量比AVL,红黑树等数据结构要多,所以他每次进行IO 的时候,就可以一次读取多个关键子和孩子所指向的文件数据块。且B树的每个节点的m一般系统会设置成1024,至于为什么,深入了解过文件系统的伙伴大概知道(这里我自己也是看到过网上有人说,至于原因,个人了解的也不太清楚,好像是跟文件系统的什么大小有关来着,也是1024,知道的伙伴可以说一下),这样就大大减少了B树的高度,最差情况下,也是进行高度次IO,而此时的IO次数最多也就是4次。有了代码就好理解了。
2024-03-02 00:10:09 1364 1
原创 c++前置前置声明(函数与类)
c++是以c的为基础进行后来改进的,而学过c语言的都知道,在c语言中,有时我们不可避免的要用声明,因为有时候我们在定义函数的时候,函数的定义写在了.cpp或是.c文件,而函数的声明我们一般放到了头文件中。很简单的一个原因,就是虽然我们使用了前置声明来告诉编译器存在A这个类,但是编译器此时并不知道的是A这个类的大小是多少,具体有哪些成员函数或者成员变量,所以我们在fun这个函数中使用A类实例化对象的时候,无法使用。声明是什么意思,就不多说了,很简单,也很好理解,但是声明也是有大坑的。我们先看看下面的代码。
2024-02-25 22:46:56 556 1
原创 C++make_pair,你真的懂了吗?
其次,这个代码压根就是错误的,都过不了编译,怎么跑,所以绕来绕去就又回来了,为啥错了,其实这里在vs上可以清楚的看到,虽然你显示的传了string&,但是编译器会给你加一个std::remove_reference_t这个类型,它的功能是去除引用,所以此时构建的pair类型是pair<string,string>的,所以导致编译出现报错,所以这理最好是使用pair,先不说不方便还是其他的,就光光是把make_pair显示的写出了模版类型,这就已经和pair差不多了,且make_pair还是错误的。
2024-01-17 21:29:00 2092
原创 epoll实现(ET)
这个错误一出来我就很疑惑,因为代码被我改正确了,所以错误不一样,但是大体就是这样的 ,还有就是显示的是没有这个类型名,我看了看我自己的头文件,都包含了,但是还是显示错误,然后我就上网查了一下,网上说的大概是头文件重复包含了,但是我的每个头文件都有#pragma once防止重复包含的,所以我就一直在看,以为编译过不了,连日志也不能打,只能干看,最后才发现是有几个函数的定义我放在了头文件,导致两个文件互相包含,这样的话,连声明函数都不知道怎么声明,后来才写成来两个.cpp的文件。所以,这里明显提高了效率。
2024-01-16 23:13:20 505
原创 arp欺骗原理以及实现方式
所以我就只进行了读取,但是如果你想测试的话,可以试试,用sendto就可以,但是在用sendto时,我们有几个地方与udp中使用的不一样,这个我就不在这里详细说了,想知道的小伙伴可以私信问或是上网查查。换句话来说,在同一个局域网内,我们要黑掉一个主机,只需要构建大量的arp应答,把自己的mac地址发送给要黑掉的主机,这样他发送的所有数据都会经过你的主机,如果你不管,也不转发,那么,恭喜你,成功让他的主机无法上网了。我开了两个端口,一个是用来删除arp缓存中的默认网关的地址的,一个来运行。
2023-12-14 22:24:11 299 1
原创 为什么要有arp以及arp原理
在学网络的时候,我们知道的是自顶向下交付数据包。我们学过ip协议就会知道的是,ip数据包在向下交付的时候,我们已经对查过路由表了,也就是说我知道这个数据包要去哪,但是我们数据包在网络上务必要经过路由器等节点,我们经过节点的时候会用到mac地址,但是对方的mac地址我们应该如何得知?那么,此时问题来了,这样的话,局域网中是否是有大量的arp请求,其实不这样的,因为arp层会有一个缓存,它对应的是以前所应答的ip地址所对应的mac地址,可以理解为他是具有记忆的,但是这个记忆是有时间限制的。
2023-12-14 21:52:01 185
原创 网络计算机模拟实现
其次而来的问题就是,因为这个线程池是多生产者多消费者的,所以我就想的是提高一点效率,携程了可以并发执行的模型,但是问题又来了,他们怎么插入任务,要知道的是服务端可不是生产者的角色,他只是负责把读取的人插到线程池,这就引发了一个问题,怎么插,所以我想了想,便在县城的私有成员变量中,写了一个任务和套接字的文件描述符。下面就让我们看看代码吧!如果是单生产者还好,但是我写成了多生产者和多消费者,这里就有好多问题,困扰了我很久,这个计算机我大概写了有半个月左右吧,难就难在了这个线程池的地方。下面先说说他的问题吧。
2023-12-11 13:27:33 112
原创 UDP网络套接字编程
因为再多人聊天中,我们预期是一个人发,多个人收,如果还是用这个代码的话,那么 如果开启服务端的时候,多个人你同时建立连接,那么此时如果有其中一个人发了信息,并且假设其他人都没有发信息,那么此时就会导致其他人卡在写的界面,因为他们没有写,所以没办法读信息。假设ip地址不重复。如上图,其实数据在网络中是自顶向下,然后在通过以太网的网线传输到另一个主机上,在自底向上,就可以收到了,前提是在同一个局域网中,如果不在一个局域网,肯定会经过路由器的,这里就不详细说了,主要说说我们的udp协议。其实很简单,网络有五层。
2023-11-20 23:10:19 1392 2
原创 linux线程池模拟实现
实现的时候,个人认为可以放任务的队列实现成static,如果实现成static的话,那么此时的所有对象只有一个任务队列,但是话又说回来,如果创建多个线程池的对象,那么就只有一个任务队列,貌似也不符合逻辑,有这种想法的其实错了,因为线程池就像内存池一样,只有一个,所以我最后把其设计成了单列模式,用的是饿汉模式,因为饿汉模式没有线程安全问题,所以我用了饿汉模式实现你的单利。总体实现如上,逻辑还是比较像生产消费模型的,总体进行了锁的封装,没有写打印日志的函数。
2023-11-13 22:30:08 221 2
原创 linux生产者消费者模型
这个模型充分说明了两者的关系,就是一个共用的资源,一个放,一个拿,且有三种关系,两种角色,一个交易场所。,所以弄清楚了基本的模型,我们就来看看如何实现。2.其次就是这个RAII的风格方式加锁,不知道大家写的时候有没有遇见过,就是把锁的初始化写到了构造函数(上面的代码是写到Rmtx中构造函数),其实这样写是错的,这样的话类似于加不上锁,我也测试了很长时间才发现,因为这样的话每个线程都会创建一个这个对象,所以会把这个锁初始化好多次,造成类似于没有加锁的那种情况,就是多线程共同访问临界资源,这个一定要注意。
2023-11-08 23:06:30 1005 2
原创 map,set的封装
首先就是,判断当前节点的右子树是不是空树,如果不是空树的话,那么代表的这个节点以及这个节点的做左子树已经访问完成,而右子树不为空,所还没有访问完这颗树,所以++之后就要找右子树的最左节点。相信大家都对map,set不会陌生,这是非常好用的容器,它的底层是红黑树,红黑树在数据结构中,个人感觉是比较难的一种数据结构,尤其是一会染色,一会旋转的,弄来弄去把人给弄晕了。这个思想还是有点绕人的,再就是其他的封装,如果看了我上一篇文章内容的伙伴,封装map,set应该还是很简单的。希望大家支持一下吧!
2023-10-14 20:34:08 180 2
原创 unordered_map与unordered_set的封装
这个重载函数的参数应该是K,但是你要插入的话,像unordered_map的插入是pair啊,所以,此时就必须要在unordered_map的这个文件中,调用Hash文件中的插入,在传下去。但是千万不要绝的实现unordered_map与unordered_set很简单,就像做饭一样,我们每个人都可以做出挂面,但是最后吃进嘴的感觉,味道是不一样,大厨做出来的大概率会比一个做饭小白做的好吃,而unordered_map与unordered_set就像做饭一样,想实现哈希捅不难,但是封装接口是真的难。
2023-10-14 13:52:10 148 4
原创 浅谈为什么多态只能是指针或引用
第二:因为在切片的时候,其实是发生了浅拷贝的,所以此时因该是A的对象与B的对象一样,不仅一样,如果他们中有指针,还是两个指针指向同一个地址的,但是这里明显没有,所以只有一个解释,那就是B在发生切片的时候,B的虚函数指针指向发生了变化。因为B继承了A,所以此时的B的虚函数指针指向的虚函数表与A不同,但是此时这里发生了切片,如果就像我们平时所说的,切片没有发生类型转换,只是将B类中A的那部分赋值给A的对象,那么此时按道理说赋值给A的对象的那个虚函数表,此时应该是B中重写之后的。但是不是,我们可以看看结果。
2023-09-24 19:50:21 327 2
原创 浅谈继承之默认成员函数
先说构造函数,其实不止构造函数,基类的默认成员函数,个人认为是不能被继承的(网上说法不一),因为继承之后,派生类就是一个整体,与基类没有任何关系,且派生类中不包含基类,所以他不可能继承构造函数,因为如果把基类的构造函数给继承下来了,那么,此时它只能构造基类的成员变量,那么派生类扩展的那些成员变量怎么办?相信学习过C++的哥们都知道的是,C++三座大山,分别是类与对象,继承,多态,也是面向对象的三大特性。所以,我个人认为是不可以继承的,想通这些,其实也就可以很好的理解隐藏,有各自的作用域这些知识了。
2023-09-23 17:53:12 107 2
原创 哈希表的实现(哈希捅)
如上,它的原理其实就是这样的,而哈希捅的实现其实使用链表来实现的,也就是每个桶都挂了一个单向链表,其实不仅可以挂单向链表,双向链表其实也可以挂,但是不用双向链表的原因是,双向链表比单链表的消耗大,所以才用的单链表,而在同一个桶的位置,挂数的时候,我们普遍用头插,这个就不说了,很容易理解,头插的时间复杂度是O(1)。上面就是我用哈希捅实现的哈希表,思维逻辑还是比较简单的,但是还是要注意的点。还有就是线性探测与二次探测的方法来实现哈希表,这个后面会陆续的发。本篇内容如果对你有用的话,希望带你一下赞吧!
2023-09-10 16:24:28 176
原创 手撕红黑树
其实AVL树和红黑树两个各有各的好处,是的,个人认为两个各有各的好处,因为AVL对树高比较严格,所以导致旋转点额次数就多,所以就会有额外的消耗,但是红黑树就没有这么多的消耗了,因为红黑树的上面几个规则,导致红黑树最长路径不得超过对短路径的两倍,所以,红黑树也会旋转,但是插入同等节点的条件下,红黑树旋转点次数肯定比AVL树少,但是AVL树是严格的logn,而红黑树是不太严格的logn,所以我说是各有各的好。如果不是,就把新插入的节点更新成祖父节点,依次往上更新,直到父节点为空或是父节点的颜色为黑色就停止。
2023-09-03 18:54:57 491 4
原创 手撕二叉平衡树
如上图,如果没有插入100,那么此时的二叉平衡树是平衡的,但是此时如果插入100,此时30这个节点的平衡因子是2,所以此时需要旋转来降低这棵树的高度,此时就是左旋。和左单旋一样,因为新插入的节点导致30这个节点的平衡因子为-2,所以此时就要旋转来降低这棵树的高度,此时是要右旋,这个和左旋一样,30是旋转点,25进行右旋,把25这个节点的右子树连接到30这个节点的左子树处就可以了。先把60当旋转点进行旋转,再把30当旋转点进行旋转,至于子树怎么连接,与先左旋,在右旋相同。2.链接parent指针。
2023-09-03 11:54:28 580 3
原创 STL之stack(适配器讲解以及双端队列的讲解)
所以在进行头插和头删,尾插尾删等时间复杂度是O(1)。它的底层是若干个数组组成的,然后头一个中控器,其中中控器中有遍历整个队列的迭代器,所以,我们可以知道的是,若是在中间插入删除,最好不要用双端队列,如果进行的操作是头插等,那么,此时可以用双端队列。我们可以这样理解,就是现实当中是的插排一样,上面有三个孔的,也有两个孔的,不管三个孔还是两个孔,只要我们插上对应的充电器,就可以给对应的电子产品充电。可以想一下,我们以前在学数据结构的时候,我们的stack有数组栈和链栈,所以,此时在理解,就比较好理解了。
2023-08-28 14:16:26 175 4
原创 STL之list模拟实现(反向迭代器讲解以及迭代器失效)
那就来说说实现方法吧。主要讲解的是,因为反向迭代器的实现底层是在正向迭代器的基础上实现的,所以反向迭代中的++对于正向迭代器来说,就是--,一定要区分开这个。首先是迭代器失效:list迭代器的失效与vector不同,list的迭代器在插入时不会有迭代器失效的现象,只有在删除的时候才有迭代器失效的现象,插入没有失效现象的原因,很简单,就不多说,而删除导致迭代器失效的原因是,在删除一个节点的时候,我们把这个节点所占的空间已经给释放了,所以此时指向这个节点的指针已经是野指针了,所以导致迭代器失效。
2023-08-25 21:07:52 925 2
原创 STL之vector(讲解迭代器失效,拷贝构造函数等现代版写法)
还有就是拷贝构造和赋值运算符的重载的现代版写法,其实个人建议的是,如果理解不了现代版的写法,可以按照深拷贝的思路来慢慢写,现代版的实现只不过是代码比较简洁而已,不过还是看个人喜好了,现代版的实现提高了成员函数的耦合性(个人认为不好的一点),如果出现错误的,维修成本比较大,而原始的写法就没有这种问题。我们可以看到的是,我在3这里插入了7,把所有的数后移了一位,但是,我们可以看到的是,此时原本3这个位置的数变成了7,但是迭代器的位置其实没有变,所以此时你如果不更新迭代器的话,就到导致迭代器的失效。
2023-08-24 19:57:13 307 3
原创 STL之string模拟实现
还有就是,大家一定要注意的是,在重载流插入和流提取的运算符时,个人认为插入既可以用友元函数,也可以自己写,上面有两种方法的代码,而在自己实现的过程中,一定要注意的是,cin其实是忽略空格和换行的,所以此时就要用到的是cin中的一个成员函数。而流提取不可以用友元函数,因为string中的打印是有多少打印多少,而不是遇见‘\0’就结束,这个一定要注意。首先就是,string是深拷贝,这个很简单,深浅拷贝在前面已经浅浅的说过一些了,深拷贝其实就是要让他们指向的空间不一样,这样析构的时候就没有错误了。
2023-08-24 12:58:28 153 2
原创 C++浅谈const
所以正因为是传值返回,不管你在不在这个函数的前面加const,只要是你引用,你就得加const,因为它产生的临时变量是常性,但是我个人认为是直接不要引用传值返回的函数,以为在把这个返回值返回给你的时候,这里是会出问题的,因为list是刚好不需要在迭代器类中析构,所以可以正确,如果是其他的地方呢?然后返回主题,const到底加不加,加了的话,代表传值返回的是一个常量,然后会产生一个临时变量,临时变量具有常性,不加的返回的是一个变量,但是产生的临时变量依然具有常性,所以,我认为这里是加不加都可以,不影响。
2023-07-14 17:46:22 363 6
原创 C++类与对象赋值运算符的重载(运算符的重载)
特别要注意的是运算符重载的函数中,只有赋值运算符的重载是类的默认构造函数,所以,当你不写赋值运算符的重载的时候,编译器会默认生成一个赋值运算的重载,而这个赋值运算符的重载是浅拷贝(值拷贝)。类似于上面这样,自定义类型虽然不能随便赋值,但是内置类型可以啊,内置类型就是类中的成员变量啊,所以,可以把自定义类型的赋值理解为“大事化小,小事化了”这个思想,所以我们就可以把这个函数给重载一下,可以让他的成员变量分别赋值。所以,这里就出来了个赋值运算符的重载,就是相当于函数重载一样,把赋值这个符号给重载一遍。
2023-07-14 16:26:53 294 4
原创 C++类与对象(默认成员函数之拷贝构造函数)
上面的代码是我随便写的一个类,其实在这里析构函数是没有必要写的,不影响什么,原因在上一篇文章中说过,这里就不重复了,我们言归正传,来看看主函数中我实例化了两个对象,第一个也就是t,它很好理解,我传参传了10,没有用构造函数的缺省值,那么第二个对象他会怎么初始化呢?改了下代码,我们可以清楚的看到的是,d与t的地址是一样的,而我在拷贝构造函数中,参数设置的是const Time& d,所以大家不难想象,这个d就是t的别名,所以充分的证明了实例化s的时候,调用的是拷贝构造函数。用法前面的内容里有,而且非常详细。
2023-06-30 21:18:06 395
原创 linux权限的理解
如果你进入一个公司,此时你需要与别人共同在一个文件目录下写代码或是干别的事情,但是此时有一个很头疼的文件,你们都能进入这个文件目录,说明你们都有x权限,你们写项目避免不了的是新建文件或是目录,那么此时代表的是你们肯定具有w权限,也就是说,此时这个多人共同工作的目录对你们来说是三个权限都开这的,但是,你想过没有,你你可以设置属于自己的权限,不让别人看自己的文件内容,这个很简单,你直接关闭其他的r权限就可以了,但是如果别人有这个心理呢?我新建的文件,它的权限就是这样的,这个是为什么呢?
2023-06-19 21:07:17 726 5
原创 C++类与对象(默认成员函数之析构函数)
我们前面知道的是,在实例化一个对象的时候,我们会调用一个构造函数,这个构造函数的作用是初始化我们类的成员变量,但是,有没有人想过,既然C++已经可以初始化成员变量了,那么,在我们程序即将结束的时候,是不是要把占用的内存归还给操作系统,但是你归还完以后,他这快内存可能还有一些数据是你没有清理干净的(尤其是一个函数中有在堆上开辟空间的,如果不释放这块空间就会导致内存泄漏),所以,此时的析构函数就是来清理这些数据的。我们可以看见他打印了出来,所以,我们就可以知道析构函数的作用是清理工作。而且他还不可以重载。
2023-06-18 14:48:57 129 2
原创 Linux环境下的工具(yum,gdb,vim)
yum其实是linux环境下的一种应用商店,主要用centos等版本。它也有三板斧:yum list,yum remove,yum install。当然不是说他只有这三个命令,还有yum search等等。在这直说以上三个。yum list其实是查看你所能安装的软件包,这个是根据你的yum源来决定的,这个yum源怎么说,我们可以这么理解,我们手机上的应用在下载的时候其实是在服务端上面找到对应的链接,然后再下载,其实yum源中存储的就是这些应用的链接,所以你的yum源越多,能下载的应用就越多。
2023-06-17 23:24:31 2153 4
原创 类与对象(this指针与默认成员函数之构造函数)
其实就是转换成了这个样子,注,这个样子编译是不能通过的,这个只是我们展开的样子,所以,我们平时写代码的时候的这个指针是被隐藏了的,所以这里我们要知道,还有要注意的一个点是,因为塔川的是对象的地址,所以this指针的地址是不能变的,所以,它的类型应该是*const,这个为什么我想大家应该都是知道的,C的内容我们就不在这复习了。所以这里就出现了一个this指针,它其实是隐藏起来的,当对象调用成员函数的时候,就会吧对象的地址传给他,这样理解的话,this指针其实本质是成员函数的一个形参,是个隐藏的参数。
2023-06-15 21:52:54 127 6
原创 C++类和对象
以上是它的三大权限和访问规则,这里不再多说,又不懂的小伙伴可以私聊。剩下的就是类的大小计算了,我们知道的是结构体有大小,也学结构体的大小是怎么算的,那么类的大小是怎么算的呢?其实也是很简单的,结构体中的内存对齐问题在类中还是继续用的,但是类的大小是不计算成员函数的大小的,所以,这样一来,类的大小计算方式和结构体的大小计算方式就是一样的了。好了,现在有了类,我们知道的是C++是面对对象的,C语言才是面向过程的,所以我们就算是实现了一个链表,也不可能让用的人知道里面到底是什么,所以这里就有了类的访问权限。
2023-06-06 20:07:27 74 4
原创 C++内联函数
我们明显看到,这里是没有展开的,这是为什么呢?其实这个是因为内联函数对于编译器来说只是一个建议,编译器可以选择不展开,因为展开之后,会使可执行程序变大,所以内联函数只有当指令行数比较小的时候才会展开。关键字是inline。注:递归函数是不会展开的,代码行数过大也是不会展开的,还有,内联函数最好是不要把声明和定义分开,这样子会有链接错误。首先,我们要知道的是,C++是补充了C的不足之处,所以C++是包容C的。而内联函数要想展开的话,它里面的代码行数是1-10行左右,但是我个人认为的是应该和他的汇编指令有关。
2023-05-29 15:12:02 78 4
原创 堆(堆排序以及TOPK问题)
大家想想,如果是小堆的话,要想排成升序,就必须要取堆顶的数据,取之后要删除数据,就在删除这出现了问题,有什么问题呢?所以我们建立大堆,大堆对顶的数据肯定是最大的,所以我们把堆顶的数据与数组中最后的一个元素交换,然后向下调整就好。其实堆的概念是建立在完全二叉树的基础上的,简单来说,堆就是父亲节点的值大于等于孩子节点(或是小于等于)。大家可以想一下,如果前k个元素中存在这个数据的最大的值,那么此时建立的大堆,对顶就是最大的值,这样的话就无法让次大值入堆了,所以要建立成小堆。以上就是堆排序的实现,原理很简单。
2023-05-06 14:42:11 106 5
原创 平衡二叉树的实现(包含旋转)
我们知道,在学时间复杂度的时候,我们一般都是根据最坏的结果来计算的,那么,它的最差的时间复杂度用大O表法,是不是就是O(n),但是如果这是一个平衡二叉树的话,那么,这个平衡二叉树的搜索时间就是O(log n)。好了,旋转的思想给大家说完,那么就是代码了,上面的代码是调试的时候可以清楚的看到,但是唯一不好的地方在于必须提前把最小不平衡子树找到,然后传入不平衡节点的地址,还有找到他的前驱,以方便旋转完成之后继续衔接。注意的是旋转的节点如果有左子树或是右子树,我们应该提前保存它,旋转之后在插入。
2023-04-28 13:07:03 439
空空如也
二叉树左右子树的交换
2023-04-01
TA创建的收藏夹 TA关注的收藏夹
TA关注的人