看了左神的很多视频,感觉一些算法最好的复现方式应该是用一张张的图来细节刻画,个人感觉这种效果会比动态图要好。
故在此先将全部的笔记附到这里,后续在一点一点把过程图复原完整(暂时没研究手绘软件)。
本质上也是在求一个累加;
// 如图最差的情况,空间复杂度为 O( N )
【二叉树展开,空间复杂度为log N】:因为左侧申请的空间递归结束后,可以提供给右侧使用;
//左图即为——二叉树展开;
//改成迭代的话,需要我们自己压栈;
- 所以这个问题就变成了:我划分值的位置决定了我使用空间的多少;
- 最差的是O( N ) , 最好的是O( log N );
和时间复杂度一样,求每种情况出现的概率累加, 数学上的长期期望为——O( log N )
【2】:完全二叉树 【完全二叉树】:
//上述三张图片,都叫做完全二叉树~~~!!!
【非完全二叉树】:如果跳过左侧节点,直接在右侧生出枝干,这就不是完全二叉树;
【下图就不是完全二叉树】:
//跳过了左侧节点~~~!!!
//没有从左往右依次变满~~~!!!
【完全二叉树 与 数组】:你把数组从 0 出发的连续一段儿,可以对应成完全二叉树;
- 树的节点不写值,只写下标的话:
// size=7 ; 表示从0出发连续七个位置作为完全二叉树的节点;——即:连续的一段可以用size来描述。
【完全二叉树的理解】:- 《@@24_1》
- 《@@24_2》
- i位置的左孩子/右孩子/父节点
有了这三个公式的话,我们就能够把数组中的 《 从0出发连续的一段 》 脑补成一颗完全二叉树~~~!!!
【3】:堆结构 【堆的逻辑概念】://堆在逻辑概念上是——完全二叉树。
【堆】:堆是一个特殊的完全二叉树;堆在逻辑概念上,其实是一颗完全二叉树结构;
【左神原话】:(对堆这一数据结构的经典解释!!!)
首先堆必须是一棵完全二叉树,其次堆分为大根堆 和 小根堆。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iUaTWT8N-1663993283147)(/Users/yuguangyao/Library/Application Support/typora-user-images/image-20220416173151054.png)]
【大根堆】:每一个头节点都是其子树上的最大值;
// 左图即为一个大根堆~ ~ ~! ! !
// 6 是其两颗子树上的最大数;
// 5 是其两颗子树上的最大数;
// 4 是其两颗子树上的最大数;
。。。所有节点都是其两颗子树上的最大数——此即大根堆。
【小根堆】:。。。所有节点都是其两颗子树上的最小数——此即小根堆。
【 完全二叉树 调整成 堆 】:- 《@@25_3》
//视频中演示的是——用户不断给我数字,得不停地调整堆的结构。
【堆与数组】:理解了完全二叉树的形状之后,实际的结构如何实现呢?——借助数组。(从0出发的一段儿,可以对应成完全二叉树。)
如何把数组连续出发的一段儿,搞成一个堆呢?
假设:
堆外面有一个接口,用户可以通过这个接口,依次给我一些数字,我把这些数字依次放到我准备好的干净的数组上,然后我怎么样让每一个用户进来的数字,整体形成一个大根堆呢?
【规律】:新加进来的数会一直和父比较,如果一直比父节点的数要大,就一直交换,一路往上窜,直到比较失败为止。
来到了头节点位置 / 比较失败。
【 heapInsert 】:从一个位置出发,往上动的过程称之为heapInsert。
//某个数现在处在index位置 , 往上继续移动。
【调整堆结构】:【用户的需求】:
- (1)我给过的所有数字中最大值是多少?
问题(1)——直接返回堆的头节点即可~~~
- (2)我给过你的最大数是多少,然后将其在堆中去掉。
//用户想让我提供一种 *** 作~~~
- 《@@26_4》
- 《@@27_5》
从一个位置出发,往下动的过程称之为heapify ( 堆化 )。
【大根堆堆化示例】:- 《@@27_6》
- heapIfy
- heapInsert
这是最重要的两个 *** 作 , 其余的 *** 作都是从这两种 *** 作演变而来~~~
【 用户的新 *** 作 】:- 《@@28_7》
如果将某堆中的数字a变成未知数X , 那么你还能保持此时的有效区域仍然还是堆数据结构呢???
【复杂度】://用户新加一个数字,调整代价是logN级别的;heapInsert只关心我的父的路径,所以只走一个高度,所以用户新加一个数字的时候,调整代价即是logN。
//用户删掉最大值 , 并将剩下的数调整成堆——代价也是LogN级别。
【 完全二叉树的高度 】:- 《@@28_8》
- 《@@29_9》
周而复始 , 当heapSize减至0时 , 这个数组便排好序了,即——不断地将最大的数往后放,并移出堆区。
【 用户新要求 】:之前是用户一个一个给你数字,现在要求——用户一口气提供一个数组,直接搞成大根堆。
- 《@@30_10》
O( N*logN )
[ 额外空间复杂度 ]:O( 1 )
【 分析时间复杂度 】:- 《@@31_11》
优先级队列结构,就是堆结构。
起名虽然叫做优先级队列 , 但是其底层就是堆结构。——通常堆顶是优先级最大的~~~!!!
【核心】:堆排序的地位远远没有堆结构重要!!!
【堆排序扩展题目】:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-npjy6meQ-1663993283148)(/Users/yuguangyao/Library/Application Support/typora-user-images/image-20220613220227745.png)]
- 《@@32_12》
- PriorityQueue( 无参构造一定是小根堆; )
//优先级队列的意思;
【6】:堆扩容 【堆扩容】:既然底层是用数组做的我堆结构的实际结构,我虽然可以用heapSize来规定堆的有效范围,但是如果一直添加数字的话,这个数组一定有耗尽的时候;
- 2倍扩容!!!
扩容时:需要将原数组所有内容全部都拷贝到新数组中;
单次扩容的代价是 O( N );
一个数组一共从头开始加了N个数的话,需要扩容 ( log N )次;
【 堆扩容时间复杂度 】:- 《@@33_13》
需要细腻调整堆结构时,必须手写堆;
//系统堆不是不能完成这种功能,而是不会做到高效;
【系统堆】:- 图解《@@33_14》
//你给它一个,它给你一个(唯一交流方式)。
【系统堆所不支持】:已经形成了堆结构,自己人为的变动某些节点,然后又想以很轻的代价重新调整成堆结构 , 这对于系统堆来说 , 是无法完成的。
【手写堆优势】:如果某一个节点变了,我可以只针对这一个节点看是否需要——heapIfy / heapInsert .
- 很多面试场合,我不得不手写《堆》
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FSHubZVJ-1663993283148)(/Users/yuguangyao/Library/Application Support/typora-user-images/image-20220818151941943.png)]
【 比较的核心 】:- return的数的正负
【 正 】—— 二参置前
【 负 】—— 一参置前
【复杂的比较策略】:- 先按年龄 , 年龄相同按班级
new PriorityQueue<>(new IdAscendingComparator())
- 传入一个比较器即可~~~
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rsCiCike-1663993283148)(/Users/yuguangyao/Library/Application Support/typora-user-images/image-20220818155815241.png)]
【词频排序】:词频排序——又称:计数排序。
【年龄例子】:- 《@@34_14》
数组里存放的是人的年龄,因为是年龄,所以可以认为数据状况是——( 0~200 )
【词频排序的弊端】:如果数组中元素过大,上万位,那么数组的长度是不可想象的。2^ 31 长度的数组?——词频表直接爆炸~ ~ ~
【总结】:不基于比较的排序 , 全是根据数据状况作的排序 ;
- 必须分析数据状况,来改出一个特殊的版本来~
- 必须根据数据状况专门定制!!!
- 《@@34_15》
因为高位数字是晚排序的,所以其优先级最高~ ~ ~
【桶的概念】:桶就是容器的意思,桶的具体结构可以是队列,可以是数组,可以是栈,什么都行~
//在这里我们认为桶的结构是队列。
【 桶的个数 】:比数组好太多,比计数排序好太多。
如果是十进制,搞十个桶即可。三进制,搞三个桶即可。
但桶排序依然和数据状况挂钩:
数据必须要有进制这个玩意儿~ ~ ~
【进出桶的次数】:完全是由数组中最大值的位数决定~ ~ ~
最大值的位数为N。 <==> 所有数字进出桶的次数。
100 ----> 3次;
1000 ----> 4次;
10000 ----> 5次;
【9】:前缀和数组思想
- 基数排序__radixSort讲解
- 《@@36_16》
我们利用这个count数组,其实可以做到分片儿~~~
原数组从右往左,根据count[] 来输入到 bucket[] 中,实际上就相当于完成了一次入桶 / 出桶;
利用词频表来完成进出桶~~~
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)