zoukankan      html  css  js  c++  java
  • 《数据结构与算法分析:C语言描述》复习——第十章“算法设计技巧”——跳表

    2014.07.07 22:03

    简介:

      跳表(skip list)是一种随机化的有序数据结构。从形状上来看,长得比较像分层索引。能够在接近对数级别的时间内完成增、删、改、查操作。

      你姑且可以认为这种数据结构的用途、用法都和平衡树很相似,但内部的实现原理则完全不同。

    图示:

      下面是一条有序的单链表:

      

      如果你要查找某一个整数,必然需要O(n)级别的时间去搜索链表。

      但如果从这个链表抽取几个元素,然后在上面加上一层呢,比如这样:

      

      上面的双层链表的上层是通过从下层抽取一部分元素得到的。并且上下之间也存在指针链接,允许由上至下、由左至右的遍历。

      这么做有什么用呢?我们先继续看看这个:

      

      我们可以像这样,从每一层选出部分元素,在正上方组成新的一层,这样就能够形成一个看起来不整齐的多层链表。

      这个图形很不规则,我们来考虑一个规则的情况:如果这个多层链表恰好有5层,每层元素个数分别为1,2,4,8,16。那么查找任意一个元素是不是能够在对数时间内完成呢?

      答案当然是可以。因为多层索引的代表结构——B+树就是这种形状规则而且有序的分层结构。稳定的对数级别的操作时间就是它们的最大优势。

      

      跳表使用另一种思维方式来构造一个类似的分层结构,但是因为包含了随机数而导致形状没那么“规则”,所以效率接近对数级别,但不稳定。

      刚才我们的描述方式是一层一层地构建了这个多层链表,而正确的理解方式应该是——一个一个地插入节点。关键在于每个插入的节点有可能只插入一层,也有可能插入多层。

      下面是一个空的跳表:

      

      其中begin表示一个实际的节点,尽管其中的数据没有意义,我们需要它的指针进行移动。end则可以用空指针NULL来代表,表示链表尾。

      下面如果我们要插入一个整数34,并且插入的高度是3层,结果将是这样:

      

      如此一来有了3个34。接下来我们再插入5,插入1层:

      

      那么插入的规则是怎么定的呢?比如将元素x插入到第k层?

      如果我们将最底层定义为1层,那么x元素将被插入到1~k的所有层中。

      当你从跳表的左端顶部的begin节点出发时,在每一层你都只能至多向右或向下走一步。

      由于每一层的链表都是有序的,所以插入元素时每一层的操作就和有序单链表的操作一样了。

      于是我们从第k层开始插入,完成后下降到k-1层,逐层插入直到最底层。

      下面我们再插入元素7,高度为2:

      

      那么,每个元素插入的高度怎么决定呢?随机定就行了。此处“随机”的方法,指的是抛硬币。

      如果每次有50%的概率抛出正面,那么我们就一直抛硬币,抛出正面为止,看看到底要抛多少次。将抛硬币的次数作为高度(至少抛一次,所以高度至少为1)。

      这样的随机方法严格服从于p=0.5的几何分布。因此期望高度为2,虽然偶尔也能出现很高的层数。

      那么,这种多层的结构并不是严格的二分查找,为何它的基本操作的效率接近对数级呢?

        1. 越是在高层,移动一步就能跨过越多的元素,步子越大速度越快

        2. 分析一下几何分布的分布列,此处从概率上是符合二分规律的,因此“接近对数”是有理论支持的

      

      教材上对于跳表的评价是:一种平衡树的替代品,实现简便,效率不错。

      亲自实现之后,发现跳表虽然也没那么简单,但的确比AVL树简单多了。也许我们基本不需要亲自写这些结构,但至少应该了解这种随机化的数据结构为何如此巧妙。

      

      在我的印象里,这本书的第十章尽管充满了精华知识,在我大二的数据结构课程里,老师却几乎只字未提。

      就我们学校而言,计算机教育实在是囫囵吞枣。课程好几十门,没有一门深入过。难怪我现在复习这本教材几乎等于重新学习。

      呵呵的是教育,惭愧的是自己。你区区一个本科生,既然什么都不会就别拽了。赶紧学习吧。

      (跳表还可以进一步优化为确定性跳表,不过那部分我已经无力研究了,饶了我吧)

    实现:

      1 // My implementation for skip list.
      2 #include <cstdlib>
      3 #include <ctime>
      4 #include <iostream>
      5 #include <string>
      6 using namespace std;
      7 
      8 template <class TKey>
      9 struct ListNode {
     10     TKey *key;
     11     ListNode *down;
     12     ListNode *next;
     13     
     14     ListNode(): key(nullptr), down(nullptr), next(nullptr) {}
     15 };
     16 
     17 template <class TKey>
     18 class SkipList {
     19 public:
     20     SkipList() {
     21         m_root = new ListNode<TKey>();
     22         m_size = 0;
     23         m_level = 0;
     24     }
     25     
     26     bool contains(const TKey &key) {
     27         if (m_size == 0) {
     28             return false;
     29         }
     30         
     31         ListNode<TKey> *ptr = m_root;
     32         while (true) {
     33             if (ptr->next != nullptr) {
     34                 if (key < *(ptr->next->key)) {
     35                     if (ptr->down != nullptr) {
     36                         ptr = ptr->down;
     37                     } else {
     38                         return false;
     39                     }
     40                 } else if (key > *(ptr->next->key)) {
     41                     ptr = ptr->next;
     42                 } else {
     43                     return true;
     44                 }
     45             } else {
     46                 if (ptr->down != nullptr) {
     47                     ptr = ptr->down;
     48                 } else {
     49                     return false;
     50                 }
     51             }
     52         }
     53     }
     54     
     55     void insert(const TKey &key) {
     56         if (contains(key)) {
     57             return;
     58         }
     59         
     60         ListNode<TKey> *ptr;
     61         int new_level = _randomLevel();
     62         
     63         if (new_level > m_level) {
     64             // Extra levels need to be added.
     65             for (int i = m_level; i < new_level; ++i) {
     66                 ptr = new ListNode<TKey>();
     67                 ptr->down = m_root;
     68                 m_root = ptr;
     69             }
     70             m_level = new_level;
     71         }
     72         
     73         int lvl = m_level;
     74         ListNode<TKey> *last, *cur;
     75         
     76         ptr = m_root;
     77         last = cur = nullptr;
     78         while (true) {
     79             if (ptr->next != nullptr) {
     80                 if (key < *(ptr->next->key)) {
     81                     if (lvl <= new_level) {
     82                         cur = new ListNode<TKey>();
     83                         if (last == nullptr) {
     84                             cur->key = new TKey(key);
     85                         } else {
     86                             cur->key = last->key;
     87                             last->down = cur;
     88                         }
     89                         last = cur;
     90                         cur->next = ptr->next;
     91                         ptr->next = cur;
     92                     }
     93                     
     94                     if (ptr->down != nullptr) {
     95                         ptr = ptr->down;
     96                         --lvl;
     97                     } else {
     98                         break;
     99                     }
    100                 } else if (key > *(ptr->next->key)) {
    101                     ptr = ptr->next;
    102                 } else {
    103                     break;
    104                 }
    105             } else {
    106                 if (lvl <= new_level) {
    107                     cur = new ListNode<TKey>();
    108                     if (last == nullptr) {
    109                         cur->key = new TKey(key);
    110                     } else {
    111                         cur->key = last->key;
    112                         last->down = cur;
    113                     }
    114                     last = cur;
    115                     cur->next = ptr->next;
    116                     ptr->next = cur;
    117                 }
    118                 
    119                 if (ptr->down != nullptr) {
    120                     ptr = ptr->down;
    121                     --lvl;
    122                 } else {
    123                     break;
    124                 }
    125             }
    126         }
    127         ++m_size;
    128     }
    129     
    130     void erase(const TKey &key) {
    131         if (!contains(key)) {
    132             return;
    133         }
    134         
    135         ListNode<TKey> *ptr = m_root;
    136         ListNode<TKey> *cur;
    137         while (true) {
    138             if (ptr->next != nullptr) {
    139                 if (key < *(ptr->next->key)) {
    140                     if (ptr->down != nullptr) {
    141                         ptr = ptr->down;
    142                     } else {
    143                         break;
    144                     }
    145                 } else if (key > *(ptr->next->key)) {
    146                     ptr = ptr->next;
    147                 } else {
    148                     cur = ptr->next;
    149                     ptr->next = cur->next;
    150                     if (ptr->down != nullptr) {
    151                         delete cur;
    152                         ptr = ptr->down;
    153                     } else {
    154                         delete cur->key;
    155                         delete cur;
    156                         break;
    157                     }
    158                 }
    159             } else {
    160                 if (ptr->down != nullptr) {
    161                     ptr = ptr->down;
    162                 } else {
    163                     break;
    164                 }
    165             }
    166         }
    167         --m_size;
    168 
    169         ptr = m_root;
    170         while (ptr->next == nullptr) {
    171             // Empty levels are removed.
    172             if (ptr->down == nullptr) {
    173                 break;
    174             } else {
    175                 m_root = m_root->down;
    176                 delete ptr;
    177                 ptr = m_root;
    178                 --m_level;
    179             }
    180         }
    181     }
    182     
    183     size_t size() {
    184         return m_size;
    185     }
    186     
    187     void clear() {
    188         _clearUp();
    189         
    190         m_root = new ListNode<TKey>();
    191         m_size = 0;
    192         m_level = 0;
    193     }
    194 
    195     void debugPrint() {
    196         ListNode<TKey> *p1, *p2;
    197 
    198         cout << '{' << endl;
    199         p1 = m_root;
    200         while (p1 != nullptr) {
    201             p2 = p1->next;
    202             cout << "    ";
    203             while (p2 != nullptr) {
    204                 cout << *(p2->key) << ' ';
    205                 p2 = p2->next;
    206             }
    207             cout << endl;
    208             p1 = p1->down;
    209         }
    210         cout << '}' << endl;
    211     }
    212 
    213     ~SkipList() {
    214         _clearUp();
    215     }
    216 private:
    217     int m_level;
    218     int m_size;
    219     ListNode<TKey> *m_root;
    220     
    221     void _clearUp() {
    222         ListNode<TKey> *head = m_root;
    223         ListNode<TKey> *p1, *p2;
    224         
    225         while (head != nullptr) {
    226             p1 = head;
    227             head = head->down;
    228             while (p1 != nullptr) {
    229                 p2 = p1->next;
    230                 if (p1->key != nullptr && p1->down == nullptr) {
    231                     delete p1->key;
    232                 }
    233                 delete p1;
    234                 p1 = p2;
    235             }
    236         }
    237     }
    238     
    239     int _randomLevel() {
    240         int level = 0;
    241         
    242         while (rand() & 1) {
    243             ++level;
    244         }
    245         
    246         return level;
    247     }
    248 };
    249 
    250 int main()
    251 {
    252     srand((unsigned int)time(nullptr));
    253     string s;
    254     SkipList<int> sl;
    255     int key;
    256     
    257     while (cin >> s) {
    258         if (s == "i") {
    259             cin >> key;
    260             sl.insert(key);
    261         }  else if (s == "c") {
    262             cin >> key;
    263             cout << (sl.contains(key) ? "Yes" : "No") << endl;
    264         } else if (s == "e") {
    265             cin >> key;
    266             sl.erase(key);
    267         } else if (s == "cl") {
    268             sl.clear();
    269         }
    270         sl.debugPrint();
    271     }
    272     sl.clear();
    273     
    274     return 0;
    275 }
  • 相关阅读:
    发现个atan2的正确使用方式
    Forward+ Shading架构
    fatal: unable to connect to gitee.com: gitee.com[0: 180.97.125.228]: errno=Unknown error 解决方案
    HDFS HA(高可用性)集群规划
    如何使用RTP引擎对语音编码进行转码
    关于 Angular 应用 tsconfig.json 中的 target 属性
    浅谈 Orbeon form builder 的权限控制
    关于 Angular 应用 tsconfig.json 中的 lib 属性
    orbeon form 通过 url 的方式同第三方应用集成的开发明细
    orbeon form 的配置介绍
  • 原文地址:https://www.cnblogs.com/zhuli19901106/p/3830665.html
Copyright © 2011-2022 走看看