跳表和散列
对于一个有n个元素的有序数组,用折半搜索法进行搜索所需要的时间为O(log n),而对一个有序链表进行搜索所需要的时间为O(n)。
我们可以通过对有序链表上的全部或部分节点增加额外的指针,来提高搜索性能。
增加了向前指针的链表叫作跳表。跳表不仅能提高搜索性能,同时也可以提高插入和删除操作的性能。它采用随机技术决定链表中哪些节点应增加向前指针以及在该节点中应增加多少个指针。
散列法是用来搜索,插入,删除记录的另一种随机方法。与跳表相比,它的插入和删除操作时间提高到O(1),但最坏情况下仍为O(n)。
注:在经常将所有元素按序输出或按序号搜索元素时,跳表的执行效率将优于散列。
字典是一些元素的集合。每个元素有一个称为
key
的域,不同元素的
key
各不相同。
结构中有一组有层次的链,0级链是包含所有元素的有序链表,1级链表是0级链的一个子集。I级链所包含的元素是i-1级链的子集。这样的结构就是跳表。
级的分配:
i-1级元素属于i级元素的概率为p;
MaxLeve最大值:log1/p(N)-1;
CutOff=p*RAND_MAX;
Int lev=0;
While(rand()<=CutOff)lev++;
如:
template<class E, class K>
template<class E, class K>
Int SkipList<E,K>::Level()
{
//产生一个随机级号,该级号<=MaxLevel
Int lev=0;
While(rand()<=CutOff)lev++;
Return (lev<=MaxLevel)?lev:MaxLevel;
}
字典另一种描述方法就是散列(hash),它是用一个散列的函数把关键字映射到散列表(hash table)中的特定位置。
线性开型寻址散列和链表散列
(1) 当关键字的范围太大,不能用理想方法表示时,可以采用散列函数。但采用散列函数发生空间碰撞和溢出时,存储到下一个可用的桶中,这种解决溢出的方法叫作线性开型寻址。
搜索起始桶f(k),接着对表中的后继桶进行搜索。直到发生以下情况:1〉存在关键字为K的桶已找到,即找到了要搜索的元素;2〉到达一个空桶;3〉又回到f(K)桶。若发生后两种情况,则说明表中没有关键字为k的元素。
(2) 当散列发生溢出时,链表是一种好的解决方法,在散列表的组织中,每个桶仅含有一个节点指针,所有的元素都存储在该指针指向的链表中。
跳表和散列
跳表和散列均使用了随机过程来提高字典操作的性能。
在使用跳表时,插入操作用随机过程来决定一个元素的级数。这种级数分配不考虑要插入元素的值。
在散列中,当对不同元素进行插入时,散列函数随机地为不同元素分配桶,但散列函数需要使用元素的值。
**
不过跳表比散列更灵活。例如
,
只需简单地沿着
0
级链就可以在线性时间内按升序输出所有的元素。而采用链表散列时,需要
O
(
D+n
)时间去收集
n
个元素并且需要
O(nlogn)
时间进行排序,之后才能输出。对于其他的操作。如查找或删除最大或最小元素,散列可能要花费更多的时间。