跳表
1 概念
对于一个有n个元素的有序数组,用折半搜索法进行搜索所需要的时间为O(log n),而对一个有序链表进行搜索所需要的时间为O(n)。
我们可以通过对有序链表上的全部或部分节点增加额外的指针,来提高搜索性能。 通常节点结构中有一组有层次的链,0级链是包含所有元素的有序链表,1级链表是0级链的一个子集。I级链所包含的元素是i-1级链的子集。这样的结构就是跳表。
增加了向前指针的链表叫作跳表。跳表不仅能提高搜索性能,同时也可以提高插入和删除操作的性能。它采用随机技术决定链表中哪些节点应增加向前指针以及在该节点中应增加多少个指针。
2 级的分配:
i-1级元素属于i级元素的概率为p; p通常取0.5
MaxLeve最大值:log1/p(N) - 1; N是元素个数
CutOff=p*RAND_MAX;
Int lev=0;
While(rand()<=CutOff) lev++;
如:
template<class E, class K>
Int SkipList<E,K>::Level()
{ //产生一个随机级号,该级号<=MaxLevel
Int lev=0;
While(rand()<=CutOff) lev++;
Return (lev <= MaxLevel)?lev:MaxLevel;
}
或如下:
long newLevel = 0;
while ( (newLevel < maxLevel - 1) && (drand48() < probability) ) {
newLevel++;
}
3 复杂性分析
跳表中有n个元素时,搜索,插入,删除操作的平均复杂性为O(log n).
- /*
- * File :skiplist.h
- * Interface for SkipList and SkipListElement
- * Author: cxf
- * Version: 1.0
- * Date: 02/08/2008
- * History:
- 02/08/2008; cxf; Version 1.0
- */
- #ifndef _SKIPLIST_H_
- #define _SKIPLIST_H_
- #define MAXLEVEL 6 //节点中的最大级
- #define SKIPLIST_NOT_FOUND -1 //在跳表中没有找到相应的节点时,返回的值
- typedef int Key; //键的类型
- typedef int Value; //值的类型
- /* 跳表节点 */
- class SkipListElement
- {
- public:
- SkipListElement(int _level = 0, Key _key = 0, Value _value = 0);
- ~SkipListElement();
- void SetValue(Value _value);
- Value GetValue() const;
- void SetKey(Key _key);
- Key GetKey() const;
- void SetLevel(int _level);
- int GetLevel() const;
- void SetElement(int _level, SkipListElement *_next);
- SkipListElement* GetElement(int _level) const;
- private:
- Key key; //键
- Value value; //值
- int level; //本节点的最大级,共有level + 1级,从0到level
- SkipListElement **next;
- };
- /* 跳表类 */
- class SkipList
- {
- public:
- SkipList(SkipListElement *_head = NULL);
- ~SkipList();
- void Insert(const Key _key, const Value _value);
- Value Search(const Key _key);
- void Free(Key _key);
- friend std::ostream& operator<<(std::ostream out, const SkipList &slist);
- private:
- int GetNewLevel();
- SkipListElement *head; //跳表的头节点
- };
- #endif
- /*
- * File :skiplist.cpp
- * Implemention for SkipList and SkipListElement
- * Author: cxf
- * Version: 1.0
- * Date: 02/08/2008
- * History:
- * 02/08/2008; cxf; Version 1.0
- */
- #include <cassert> //assert
- #include <climits> //INT_MAX
- #include <iostream>
- #include <ctime> //time
- #include "skiplist.h"
- using namespace std;
- SkipListElement::SkipListElement(int _level /* = 0 */, Key _key /* = 0 */, /
- Key _value /* = 0 */)
- {
- level = _level;
- key = _key;
- value = _value;
- //动态分配指针数组空间
- next = (SkipListElement**)malloc(sizeof(SkipListElement*) * (level + 1));
- if (NULL == next)
- {
- cout << __FILE__ << ":" << __LINE__ << " " << "no enough memory!" << endl;
- exit(-1);
- }
- for (int i = 0; i <= level; i++)
- {
- next[i] = NULL;
- }
- }
- SkipListElement::~SkipListElement()
- {
- free(next);
- }
- void SkipListElement::SetKey(Key _key)
- {
- key = _key;
- }
- void SkipListElement::SetValue(Value _value)
- {
- value = _value;
- }
- void SkipListElement::SetElement(int _level, SkipListElement *_next)
- {
- assert(_level >= 0 && _level <= level);
- next[_level] = _next;
- }
- SkipListElement* SkipListElement::GetElement(int _level) const
- {
- assert(_level >= 0 && _level <= level);
- return next[_level];
- }
- Key SkipListElement::GetKey() const
- {
- return key;
- }
- Value SkipListElement::GetValue() const
- {
- return value;
- }
- int SkipListElement::GetLevel() const
- {
- return level;
- }
- /* 若级别比当前的高,需要重新分配空间,否则,不必释放空间,留作以后用*/
- void SkipListElement::SetLevel(int _level)
- {
- if (_level > level)
- {
- //重新分配空间
- next = (SkipListElement**)realloc(next, (_level + 1) * sizeof(void *));
- if (NULL == next)
- {
- cout << __FILE__ << ":" << __LINE__ << " " << "no enough memory!" << endl;
- exit(-1);
- }
- //初始化增加的空间
- memset(next + (level + 1), 0, sizeof(void *) * (_level - level));
- }
- level = _level;
- }
- /*===========================以下为SkipList部分===================================*/
- SkipList::SkipList(SkipListElement *_head /* = NULL */)
- {
- if (NULL == _head)
- {
- head = new SkipListElement();
- //头节点的key设置为无限大
- head->SetKey(INT_MAX);
- }
- else
- {
- head = _head;
- }
- }
- SkipList::~SkipList()
- {
- SkipListElement *element = head;
- //从级0开始,依次删除所有的节点
- while (element != NULL)
- {
- SkipListElement *temp = element->GetElement(0);
- delete element;
- element = temp;
- }
- }
- /* 随机产生一个级,为产生的新节点用 */
- int SkipList::GetNewLevel()
- {
- static int i = 0;
- /* 因为插入新节点的时间间隔几乎为零,为产生不同的随机数,加入静态变量
- * 以使每次该函数被调用时种子不同
- */
- srand(time(0) + i);
- i += 20;
- return rand() % MAXLEVEL + 1;
- }
- /* 将键为_key,值为_value的节点插入到跳表中
- * 算法思想:可将跳表看做是一系列普通的链表,在跳表中插入节点,就是寻找插
- * 入节点在每一级中的插入位置(并保存), 然后根据插入节点的级n,在0-n级的n+1
- * 条普通链表中依次插入节点
- */
- void SkipList::Insert(const Key _key, const Value _value)
- {
- SkipListElement *elemnet = head;
- SkipListElement *next = NULL;
- SkipListElement update(MAXLEVEL);//保存每一级插入位置
- int newLevel = 0; //新节点的所分配的级
- int i = 0;
- //从头节点开始扫描所有级
- for (i = head->GetLevel(); i >= 0; i--)
- {
- next = elemnet->GetElement(i);
- while (next != NULL && _key > next->GetKey())
- {
- elemnet = next;
- next = elemnet->GetElement(i);
- }
- //保存每一级指针
- update.SetElement(i, elemnet);
- }
- elemnet = elemnet->GetElement(0);
- //若键为_key的节点存在跳表中,更新其值
- if ((elemnet != NULL) && (_key == elemnet->GetKey()))
- {
- elemnet->SetValue(_value);
- }
- //不存在键为_key的节点
- else
- {
- //产生一个级
- newLevel = GetNewLevel();
- //若新节点的级比头节点的级大,更新头节点的级
- if (newLevel > head->GetLevel())
- {
- for (int i = head->GetLevel() + 1; i <= newLevel; i++)
- {
- update.SetElement(i, head);
- }
- head->SetLevel(newLevel);
- }
- //构造新的节点
- elemnet = new SkipListElement(newLevel, _key, _value);
- //调整新节点每一级的指针,以及该节点前后的指针关系
- for (i = 0; i <= newLevel; i++)
- {
- elemnet->SetElement(i, update.GetElement(i)->GetElement(i));
- update.GetElement(i)->SetElement(i, elemnet);
- }
- }
- }
- /* 从跳表中寻找键为_key的节点的值 */
- Value SkipList::Search(const Key _key)
- {
- SkipListElement *elemnet = head;
- SkipListElement *next = NULL;
- for (int i = head->GetLevel(); i >= 0; i--)
- {
- next = elemnet->GetElement(i);
- while ((next != NULL) && (_key > next->GetKey()))
- {
- elemnet = next;
- next = elemnet->GetElement(i);
- }
- //存在要找的节点,停止搜索并立即返回
- if ((next != NULL) && (_key == next->GetKey()))
- {
- return next->GetValue();
- }
- }
- //不存在要找的节点
- return SKIPLIST_NOT_FOUND;
- }
- /* 从跳表中删除键值为_key的节点 */
- void SkipList::Free(Key _key)
- {
- SkipListElement *element = head;
- SkipListElement *next = NULL;
- SkipListElement temp(MAXLEVEL); //保存欲删除节点前后的指针关系
- int i = 0;
- //从头节点开始扫描所有级
- for (i = head->GetLevel(); i >= 0; i--)
- {
- next = element->GetElement(i);
- //若_key > next->GetKey(),则转入下一个节点扫描剩下的级
- while ((next != NULL) && (_key > next->GetKey()))
- {
- element = next;
- next = next->GetElement(i);
- }
- //temp保存每一级中,欲删除节点的前一个节点指针
- temp.SetElement(i, element);
- }
- element = element->GetElement(0);
- //找到节点
- if (element != NULL && element->GetKey() == _key)
- {
- for (i = 0; i <= element->GetLevel(); i++)
- {
- //修改节点间的指针关系
- temp.GetElement(i)->SetElement(i, element->GetElement(i));
- }
- //删除本节点
- delete element;
- //修改头节点的级,若头节点中某一级的指针为空,则减一级
- while ((head->GetLevel() > 0) && (head->GetElement(head->GetLevel()) == NULL))
- {
- head->SetLevel(head->GetLevel() - 1);
- }
- }
- }
- /* 输出跳表,形式如下:每一行第一个是头节点(键是一个很大的数值)
- 2147483647-->10-->NULL
- 2147483647-->10-->20-->NULL
- 2147483647-->10-->15-->20-->30-->NULL
- 2147483647-->10-->15-->20-->25-->30-->NULL
- 2147483647-->10-->15-->20-->25-->30-->NULL
- */
- ostream& operator<<(ostream out, const SkipList &slist)
- {
- SkipListElement *element = slist.head;
- for (int i = slist.head->GetLevel(); i > 0; i--)
- {
- element = slist.head;
- while (element != NULL)
- {
- cout << element->GetKey() << "-->";
- element = element->GetElement(i);
- }
- cout << "NULL" << endl;
- }
- return out;
- }