赞
踩
堆是完全二叉树,根据结点和左右孩子的大小关系,分为最大堆和最小堆
二分查找适用于有序数组
通过中序遍历即可得出有序的序列, 构造一棵二叉搜索树的目的:为了提高查找和插入删除关键字的速度
平衡二叉树是二叉搜索树的一种,其特点:左右子树的高度之差的绝对值不超过1,左右子树高度之差被称为平衡因子,每次插入一个新的值的时候,都要检查二叉树的平衡,也就是平衡调整
将二叉树上结点的左子树深度减去右子树深度的值称为平衡因子BF (Balanoe Factor),平衡因乎只可能是-1、0和 1
查找时间复杂度O(logn),插入和删除时间复杂度O(logn)
红黑树(red-black tree) 是一棵满足下述性质的二叉查找树:
- 每个结点非红即黑
- 根结点为黑色
- 每个叶结点(叶结点即实际叶子结点的NULL指针或NULL结点)都是黑的;
- 若结点为红色,其子节点一定是黑色(没有连续的红结点)
- 对于每个结点,从该结点到其后代叶结点的简单路径上,均包含相同数目的黑色结点(叶子结点要补充两个NULL结点)
- 有了五条性质,才有一个结论:通过任意一条从根到叶子简单路径上颜色的约束,红黑树保证最长路径不超过最短路径的二倍,因而近似平衡。
应用场景主要是STL中map,set的实现,优点在于支持频繁的修改,因为查询删除插入时间复杂度都是logN
平衡树和红黑树的区别
为什么红黑树的插入、删除和查找如此高效?
红黑树为什么要保证每条路径上黑色结点数目一致?
参考:
B-树就是B树,都是 B-tree 的翻译,里面不是减号-,是连接符-
B树的时间复杂度与二叉树一样,均为O(logN)
磁盘读取依靠的是机械运动,分为寻道时间、旋转延迟、传输时间三个部分,这三个部分耗时相加就是一次磁盘IO的时间,大概9ms左右。这个成本是访问内存的十万倍左右;
正是由于磁盘IO是非常昂贵的操作,所以计算机操作系统对此做了优化:预读;每一次IO时,不仅仅把当前磁盘地址的数据加载到内存,同时也把相邻数据也加载到内存缓冲区中。因为局部预读原理说明:当访问一个地址数据的时候,与其相邻的数据很快也会被访问到。每次磁盘IO读取的数据我们称之为一页(page)。一页的大小与操作系统有关,一般为4k或者8k。这也就意味着读取一页内数据的时候,实际上发生了一次磁盘IO。
数据库索引是存储在磁盘上,当表中的数据量比较大时,索引的大小也跟着增长,达到几个G甚至更多。当我们利用索引进行查询的时候,不可能把索引全部加载到内存中,只能逐一加载每个磁盘页,这里的磁盘页就对应索引树的节点。每个结点加载一次磁盘io,减少磁盘IO的次数就必须要压缩树的高度,让瘦高的树尽量变成矮胖的树。平衡二叉树由于树深度过大而造成磁盘IO读写过于频繁,进而导致效率低下。为了减少磁盘IO的次数,就你必须降低树的深度
B树这种数据结构常常用于实现数据库索引,查找效率比较高。
m阶B-Tree满足以下条件:
每个节点最多拥有m个子树
根节点至少有2个子树
分支节点至少拥有m/2颗子树(除根节点和叶子节点外都是分支节点)
所有叶子节点都在同一层、每个节点最多可以有m-1个key,并且以升序排列
如:一个3阶的B树
在本例中每一个父节点都出现在子节点中,是子节点最大或者最小的元素
每个叶子节点都有一个指针,指向下一个数据,形成一个有序链表。
B+树是B树的一个升级版,相对于B树来说B+树更充分的利用了节点的空间,让查询速度更加稳定,其速度完全接近于二分法查找。
为什么说B+树查找的效率要比B树更高、更稳定;我们先看看两者的区别
完B树,再来说说B+树,B+树和结构很类似,但查询性能上更高,具有如下的特性:
有k个子树的中间节点包含有k个元素(B树中是k-1个元素),每个元素不保存数据,只用来索引,所有数据都保存在叶子节点;
叶子节点中包含了全部元素的信息,按照关键字的大小从左到右排序;
从图中可以看出,B+树中间节点和叶子节点有重复的数据,这里声明一下,中间节点保存的只是子树数据的子针,并不是真实的数据,所以中间节点的存储占用空间较少。
同时,叶子节点之间用指针连在一起,换句话说,叶子节点形成了一个链表,把所有的数据都存储了进来。
为什么这样设计,比起B树有什么好处呢?
首先,因为B+树的中间节点只是保存子树的最大数据和子树的子针,本身的占用空间较小,因此可以容纳更多节点元素,也就是说同样数据情况下,B+ 树会 B 树更加“矮胖”,因此查询效率更快。
其次,查找某个范围的数据,只需在B+树的叶子节点链表中遍历即可,不需要像B 树那样挨个中序遍历比较大小。总结来说,B+树的优点就是:
总结
插入或者删除元素都会导致节点发生裂变反应,有时候会非常麻烦,但正因为如此才让B树能够始终保持多路平衡,这也是B树自身的一个优势:自平衡;B树主要应用于文件系统以及部分数据库索引,如MongoDB,所以目前大部分关系型数据库索引是使用B+树实现。
B树:有序数组+平衡多叉树;
B+树:有序数组链表+平衡多叉树;
哈希表是一种以键-值(key-indexed) 存储数据的结构,我们只要输入待查找的值即key,即可查找到其存储位置,在该位置上存储着对应的数据
字符串哈希值
int hash = 0;
char *str = "abcdef";
for(int i = 0;i < strlen(str);i++){
hash = 31*hash + (int)str[i];
}
cout<<hash<<endl;
1.拉链法(哈希桶法)
2.线性探测法(开放定址法)
基本思想:f(key) = (f(key) + d) MOD M
3.再哈希法
使用哈希函数去散列一个输入的时候,输出是同一个位置就再次散列,直至不发生冲突位置,但每次冲突都要重新散列,计算时间增加,另外需要准备多个哈希函数
4.公共溢出区法
建立一个公共溢出区域,把hash冲突的元素都放在该溢出区里。查找时,如果发现hash表中对应桶里存在其他元素,还需要在公共溢出区里再次进行查找。
设有一个哈希函数:H( c ) = c % N;
当N取一个合数时,最简单的例子是取2n,比如说取23=8,这时候
H(11100(二进制)) = H(28) = 4
H(10100(二进制)) = H(20) = 4
跳表全称为跳跃列表,它允许快速查询,插入和删除一个有序连续元素的数据链表。跳跃列表的平均查找和插入时间复杂度都是O(logn)。快速查询是通过维护一个多层次的链表,且每一层链表中的元素是前一层链表元素的子集(见右边的示意图)。一开始时,算法在最稀疏的层次进行搜索,直至需要查找的元素在该层两个相邻的元素中间。这时,算法将跳转到下一个层次,重复刚才的搜索,直到找到需要查找的元素为止
跳表的效率比链表高了,但是跳表需要额外存储多级索引,所以需要的更多的内存空间
基本思想
对于单链表来说,即使数据是已经排好序的,想要查询其中的一个数据,只能从头开始遍历链表,这样效率很低,时间复杂度很高,是 O(n)
把链表中的一些节点提取出来作为索引,为了提高效率可以逐级提取作为索引
原始链表
跳表
性质
原始链表转换成跳表
14 → 50
↓ ↓
14 → 34 → 50 → 66
↓ ↓ ↓ ↓
14 → 23 → 34 → 43 → 50 → 59 → 66 → 72
第二级索引 - 第一级索引 - 原始链表
查找
插入
void mergeSort(vector<int> &q, int l, int r){ if(l >= r) return; int mid = l + r >> 1; mergeSort(q, l, mid); mergeSort(q, mid + 1, r); static vector<int> w; w.clear(); int i = l, j = mid + 1; while(i <= mid && j <= r){ if(q[i] > q[j]) w.push_back(q[j++]); else w.push_back(q[i++]); } while(i <= mid) w.push_back(q[i++]); while(j <= mid) w.push_back(q[j++]); for(int i : w) q[l++] = i; }
int Partition(vector<int> &v,int low ,int high) { int pivot; pivot=v[low];//用子表的第一个记录作枢轴记录 while(low<high) { while(low<high && v[high]>=pivot) high--; swap(v[low],v[high]);//high指示的值小于pivot,交换 while(low<high && v[low]<=pivot) low++; swap(v[low],v[high]);//low指示的值大于pivot,交换 } return low; } void quickSort(vector<int> &v,int low ,int high) { int i; if(low<high) { i=Partition(v,low,high); quickSort(v,low,i-1); quickSort(v,i+1,high); } }
void push_down(vector<int>& heap, int size, int u){ int t = u, left = u * 2, right = u * 2 + 1; if(left <= size && heap[left] > heap[t]) t = left; if(right <= size && heap[right] > heap[t]) t = right; if(u != t){ swap(heap[u], heap[t]); push_down(heap, size, t); } } void push_up(vector<int>& heap, int u){ while(u / 2 && heap[u / 2] < heap[u]){ swap(heap[u / 2], heap[u]); u /= 2; } } void heapSort(vector<int> &q, int n){ int size = n; for(int i = 1; i <= n; i++) push_up(q, i); for(int i = 1; i <= n; i++){ swap(q[1], q[size]); size--; push_down(q, size, 1); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。