64 lines
3.4 KiB
Plaintext
64 lines
3.4 KiB
Plaintext
《核心方向》
|
|
参考别人代码,纵向是一个列表使用层索引推动,横向用各自元素的next指针往右侧推动。
|
|
|
|
《问题1:如果后来的节点随机的层数比最左侧高该怎么弄?》
|
|
目前看来,在初始化的时候就限定了最高层,所以随机不让超过此层。
|
|
|
|
《问题2:假如第二个节点很矮,是不是这样,在后边就回不到高处了》
|
|
不是必须经过矮点,可以直接 next 到后面高点。
|
|
|
|
《问题3:底层节点是有序的,比如最开始空数据时,先来一个8节点,又来个2节点,2怎么插到8前面去》
|
|
个人觉得就是找节点找到空的就往前面插入。
|
|
|
|
|
|
《atomic 相关》
|
|
1.std::memory_order_releaxed
|
|
这种语义表明加载操作并不引入额外的同步或顺序性,因此不会有额外的同步开销。
|
|
2.std::memory_order_acquire
|
|
这种语义确保了对被加载数据的后续操作符合所加载的数据顺序,即在加载数据后,
|
|
后续对返回的节点数据操作会被保证在加载操作之后执行。
|
|
3.std::memory_order_release
|
|
这种语义通常用于写操作,以确保在写入某个变量后,所有之前的写入操作在此之前都已完成。
|
|
|
|
《Leveldb中遇到的一个可变长结构体》
|
|
如
|
|
struct A { int a; char b[1];}
|
|
其中b其实是可变长的结构体,也叫柔性数组(注意b必须在结构体最后面),
|
|
实际分配内存时,给b动态分配额外的内存空间,可以使用[i]形式访问。
|
|
如果定义成 char* 那么额外的空间将不能被访问到,因为malloc的数据内容是未定义的,
|
|
为什么 char* data = (char *)malloc(4); 可以使用data[i]访问,是因为data存的是已经分配好的空间的地址,
|
|
而直接给 A malloc内存,如果定义成 char* b就如上面所说。
|
|
|
|
一.与传统指针的区别
|
|
1.b[1]的形式在编译器就预留了一定的空间,在简单足够使用时可以避免new free的额外管理操作。
|
|
2.这样写能一眼看出这个变量是一个数组。
|
|
|
|
《new 的用法》
|
|
new 可以在已经分配的空间上调用构造:
|
|
new (memory_alloc_addr) A(1);
|
|
|
|
《思路的改进点》
|
|
我默认的思路是同一个值的一个链条,结构体这样定义:
|
|
struct N {
|
|
T* k;
|
|
N* next;
|
|
};
|
|
k指向一个有真正内存的首个节点,然后再定义个下一个位置指针,
|
|
github 源码中的思路是一个结构体
|
|
struct N {
|
|
T k;
|
|
N* next; // 这个next是一个数组
|
|
}
|
|
这样一来,同一个值的数据,一个 N 就可以了,剩余的都是指针。相比较默认思路的优势是
|
|
可以直接使用下表 [i] 访问而无需一个一个next,速度更快,且在后续直接取N某一层节点数
|
|
据的逻辑中直接用,用默认next的方法,多很多不必要的逻辑。
|
|
|
|
《leveldb中跳表的实现逻辑》
|
|
一、这里说一下插入整体逻辑,这个逻辑通了,别的都OK。
|
|
1.一个数据一个 Node,该 Node 中含有一个 Node* 列表用于延伸高度。
|
|
2.在查找某个数据时,定义了一个高度的 Node* 临时列表 pre,用于记录每一层的插入点在哪个 Node。
|
|
3.pre 找好之后,new 一个保存该值的 Node x,
|
|
4.将 pre 待插入节点的下一级节点放到 x 下一级,pre 下一级改到 x。
|
|
二、这里说一下插入的细节逻辑
|
|
1.默认无数据时候,有个 Head 作用的 Node。
|
|
2.随机的高度比当前数据中的最高要高时,要将 pre 在该层的记录点指向 head |