posts - 134,comments - 22,trackbacks - 0

          本文將總結一種數(shù)據(jù)結構:跳躍表。前半部分跳躍表性質和操作的介紹直接摘自《讓算法的效率跳起來--淺談“跳躍表”的相關操作及其應用》上海市華東師范大學第二附屬中學 魏冉。之后將附上跳躍表的源代碼,以及本人對其的了解。難免有錯誤之處,希望指正,共同進步。謝謝。

              跳躍表(Skip List)是1987年才誕生的一種嶄新的數(shù)據(jù)結構,它在進行查找、插入、刪除等操作時的期望時間復雜度均為O(logn),有著近乎替代平衡樹的本領。而且最重要的一點,就是它的編程復雜度較同類的AVL樹,紅黑樹等要低得多,這使得其無論是在理解還是在推廣性上,都有著十分明顯的優(yōu)勢。

              首先,我們來看一下跳躍表的結構

           

             跳躍表由多條鏈構成(S0,S1,S2 ……,Sh),且滿足如下三個條件:

          每條鏈必須包含兩個特殊元素:+∞ 和 -∞(其實不需要)
          S0包含所有的元素,并且所有鏈中的元素按照升序排列。
          每條鏈中的元素集合必須包含于序數(shù)較小的鏈的元素集合。
             操作

             一、查找
             目的:在跳躍表中查找一個元素x
             在跳躍表中查找一個元素x,按照如下幾個步驟進行:
                1. 從最上層的鏈(Sh)的開頭開始
                2. 假設當前位置為p,它向右指向的節(jié)點為q(p與q不一定相鄰),且q的值為y。將y與x作比較
                    (1) x=y  輸出查詢成功及相關信息
                    (2) x>y  從p向右移動到q的位置
                    (3) x<y  從p向下移動一格

                3. 如果當前位置在最底層的鏈中(S0),且還要往下移動的話,則輸出查詢失敗

           

              二、插入
               目的:向跳躍表中插入一個元素x
               首先明確,向跳躍表中插入一個元素,相當于在表中插入一列從S0中某一位置出發(fā)向上的連續(xù)一段元素。有兩個參數(shù)需要確定,即插入列的位置以及它的“高度”。
               關于插入的位置,我們先利用跳躍表的查找功能,找到比x小的最大的數(shù)y。根據(jù)跳躍表中所有鏈均是遞增序列的原則,x必然就插在y的后面。
               而插入列的“高度”較前者來說顯得更加重要,也更加難以確定。由于它的不確定性,使得不同的決策可能會導致截然不同的算法效率。為了使插入數(shù)據(jù)之后,保持該數(shù)據(jù)結構進行各種操作均為O(logn)復雜度的性質,我們引入隨機化算法(Randomized Algorithms)。

               我們定義一個隨機決策模塊,它的大致內容如下:

           產生一個0到1的隨機數(shù)r     r ← random()
          如果r小于一個常數(shù)p,則執(zhí)行方案A,  if  r<p then do A
          否則,執(zhí)行方案B         else do B
               初始時列高為1。插入元素時,不停地執(zhí)行隨機決策模塊。如果要求執(zhí)行的是A操作,則將列的高度加1,并且繼續(xù)反復執(zhí)行隨機決策模塊。直到第i次,模塊要求執(zhí)行的是B操作,我們結束決策,并向跳躍表中插入一個高度為i的列。


               我們來看一個例子:
               假設當前我們要插入元素“40”,且在執(zhí)行了隨機決策模塊后得到高度為4
               步驟一:找到表中比40小的最大的數(shù),確定插入位置

           

               步驟二:插入高度為4的列,并維護跳躍表的結構

           

              三、刪除

              目的:從跳躍表中刪除一個元素x
              刪除操作分為以下三個步驟:

          在跳躍表中查找到這個元素的位置,如果未找到,則退出
          將該元素所在整列從表中刪除
          將多余的“空鏈”刪除 


              我們來看一下跳躍表的相關復雜度:
           
                 空間復雜度: O(n)       (期望)
                 跳躍表高度: O(logn)  (期望)


              相關操作的時間復雜度:
                查找:  O(logn)    (期望)
                插入:  O(logn)    (期望)
                刪除:  O(logn)   (期望)
           
              之所以在每一項后面都加一個“期望”,是因為跳躍表的復雜度分析是基于概率論的。有可能會產生最壞情況,不過這種概率極其微小。

           

          --------------------------------------------------------------------------------


              以下是自己學習時碰到的一些問題

           

              首先分配一個鏈表,用list.hdr指向,長度為跳躍表規(guī)定的最高層,說是鏈表,在以下代碼中只是分配了一段連續(xù)的空間,用來指向每一層的開始位置。我們看到結構體nodeType中,有一個key,一個rec(用戶數(shù)據(jù)),還有一個指向結構體的指針數(shù)組。


              一開始的那些圖容易給人誤解。如上圖所示,例如每個節(jié)點的forward[2],就認為是跳躍表的第3層。List.hdr的forward[2]指向11,11的forward[2]指向30,30的forward[2]指向53。這就是跳躍表的第3層:11---30-----53。(準確的說每個forward都指向新節(jié)點,新節(jié)點的同層forward又指向另一個節(jié)點,從而構成一個鏈表,而數(shù)據(jù)只有一個,并不是像開始途中所畫的那樣有N個副本)。本人天資愚鈍,看了挺長時間才把它在內存里的結構看清楚了,呵呵。  

              以下是在網上搜到的一個實現(xiàn)代碼


              代碼中主要注釋了insert函數(shù),剩下的兩個函數(shù)差不多,就不一一注釋了

             


          view plaincopy to clipboardprint?
          /* skip list */ 
           
          #include <stdio.h>  
          #include <stdlib.h>  
           
          /* implementation dependent declarations */ 
          typedef enum {  
              STATUS_OK,  
              STATUS_MEM_EXHAUSTED,  
              STATUS_DUPLICATE_KEY,  
              STATUS_KEY_NOT_FOUND  
          } statusEnum;  
           
          typedef int keyType;            /* type of key */ 
           
          /* user data stored in tree */ 
          typedef struct {  
              int stuff;                  /* optional related data */ 
          } recType;  
           
          #define compLT(a,b) (a < b)  
          #define compEQ(a,b) (a == b)  
           
          /* levels range from (0 .. MAXLEVEL) */ 
          #define MAXLEVEL 15  
           
          typedef struct nodeTag {  
              keyType key;                /* key used for searching */ 
              recType rec;                /* user data */ 
              struct nodeTag *forward[1]; /* skip list forward pointer */ 
          } nodeType;  
           
          /* implementation independent declarations */ 
          typedef struct {  
              nodeType *hdr;              /* list Header */ 
              int listLevel;              /* current level of list */ 
          } SkipList;  
           
          SkipList list;                  /* skip list information */ 
           
          #define NIL list.hdr  
          static int count = 0;  
          statusEnum insert(keyType key, recType *rec) {  
              int i, newLevel;  
              nodeType *update[MAXLEVEL+1];  
              nodeType *x;  
              count++;  
             /*********************************************** 
              *  allocate node for data and insert in list  * 
              ***********************************************/ 
           
              /* find where key belongs */ 
              /*從高層一直向下尋找,直到這層指針為NIL,也就是說 
              后面沒有數(shù)據(jù)了,到頭了,并且這個值不再小于要插入的值。 
              記錄這個位置,留著向其后面插入數(shù)據(jù)*/ 
              x = list.hdr;  
              for (i = list.listLevel; i >= 0; i--) {  
                  while (x->forward[i] != NIL   
                    && compLT(x->forward[i]->key, key))  
                      x = x->forward[i];  
                  update[i] = x;  
              }  
           
                
              /*現(xiàn)在讓X指向第0層的X的后一個節(jié)點*/ 
              x = x->forward[0];  
           
                
              /*如果相等就不用插入了*/ 
              if (x != NIL && compEQ(x->key, key))   
                  return STATUS_DUPLICATE_KEY;  
           
             /*隨機的計算要插入的值的最高level*/ 
              for (  
                newLevel = 0;   
                rand() < RAND_MAX/2 && newLevel < MAXLEVEL;   
                newLevel++);  
              /*如果大于當前的level,則更新update數(shù)組并更新當前l(fā)evel*/ 
              if (newLevel > list.listLevel) {  
                  for (i = list.listLevel + 1; i <= newLevel; i++)  
                      update[i] = NIL;  
                  list.listLevel = newLevel;  
              }  
           
              /* 給新節(jié)點分配空間,分配newLevel個指針,則這個 
              節(jié)點的高度就固定了,只有newLevel。更高的層次將 
              不會再有這個值*/ 
              if ((x = malloc(sizeof(nodeType) + newLevel*sizeof(nodeType *))) == 0)  
                  return STATUS_MEM_EXHAUSTED;  
              x->key = key;  
              x->rec = *rec;  
           
              /* 給每層都加上這個值,相當于往鏈表中插入一個數(shù)*/ 
              for (i = 0; i <= newLevel; i++) {  
                  x->forward[i] = update[i]->forward[i];  
                  update[i]->forward[i] = x;  
              }  
              return STATUS_OK;  
          }  
           
          statusEnum delete(keyType key) {  
              int i;  
              nodeType *update[MAXLEVEL+1], *x;  
           
             /******************************************* 
              *  delete node containing data from list  * 
              *******************************************/ 
           
              /* find where data belongs */ 
              x = list.hdr;  
              for (i = list.listLevel; i >= 0; i--) {  
                  while (x->forward[i] != NIL   
                    && compLT(x->forward[i]->key, key))  
                      x = x->forward[i];  
                  update[i] = x;  
              }  
           
           
              x = x->forward[0];  
           
           
              if (x == NIL || !compEQ(x->key, key)) return STATUS_KEY_NOT_FOUND;  
           
              /* adjust forward pointers */ 
              for (i = 0; i <= list.listLevel; i++) {  
                  if (update[i]->forward[i] != x) break;  
                  update[i]->forward[i] = x->forward[i];  
              }  
           
              free (x);  
           
              /* adjust header level */ 
              while ((list.listLevel > 0)  
              && (list.hdr->forward[list.listLevel] == NIL))  
                  list.listLevel--;  
           
              return STATUS_OK;  
          }  
           
          statusEnum find(keyType key, recType *rec) {  
              int i;  
              nodeType *x = list.hdr;  
           
             /******************************* 
              *  find node containing data  * 
              *******************************/ 
           
              for (i = list.listLevel; i >= 0; i--) {  
                  while (x->forward[i] != NIL   
                    && compLT(x->forward[i]->key, key))  
                      x = x->forward[i];  
              }  
              x = x->forward[0];  
              if (x != NIL && compEQ(x->key, key)) {  
                  *rec = x->rec;  
                  return STATUS_OK;  
              }  
              return STATUS_KEY_NOT_FOUND;  
          }  
           
          void initList() {  
              int i;  
           
             /************************** 
              *  initialize skip list  * 
              **************************/ 
           
              if ((list.hdr = malloc(  
                      sizeof(nodeType) + MAXLEVEL*sizeof(nodeType *))) == 0) {  
                  printf ("insufficient memory (initList)\n");  
                  exit(1);  
              }  
              for (i = 0; i <= MAXLEVEL; i++)  
                  list.hdr->forward[i] = NIL;  
              list.listLevel = 0;  
          }  
           
          int main(int argc, char **argv) {  
              int i, maxnum, random;  
              recType *rec;  
              keyType *key;  
              statusEnum status;  
           
           
              /* command-line: 
               * 
               *   skl maxnum [random] 
               * 
               *   skl 2000 
               *       process 2000 sequential records 
               *   skl 4000 r 
               *       process 4000 random records 
               * 
               */ 
           
              maxnum = 20;  
              random = argc > 2;  
           
              initList();  
           
              if ((rec = malloc(maxnum * sizeof(recType))) == 0) {  
                  fprintf (stderr, "insufficient memory (rec)\n");  
                  exit(1);  
              }  
              if ((key = malloc(maxnum * sizeof(keyType))) == 0) {  
                  fprintf (stderr, "insufficient memory (key)\n");  
                  exit(1);  
              }  
           
              if (random) {  
                  /* fill "a" with unique random numbers */ 
                  for (i = 0; i < maxnum; i++) key[i] = rand();  
                  printf ("ran, %d items\n", maxnum);  
              } else {  
                  for (i = 0; i < maxnum; i++) key[i] = i;  
                  printf ("seq, %d items\n", maxnum);  
              }  
           
              for (i = 0; i < maxnum; i++) {  
                  status = insert(key[i], &rec[i]);  
                  if (status) printf("pt1: error = %d\n", status);  
              }  
           
              for (i = maxnum-1; i >= 0; i--) {  
                  status = find(key[i], &rec[i]);  
                  if (status) printf("pt2: error = %d\n", status);  
              }  
           
              for (i = maxnum-1; i >= 0; i--) {  
                  status = delete(key[i]);  
                  if (status) printf("pt3: error = %d\n", status);  
              }  
              return 0;  

           

          本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/topcoder1234/archive/2010/08/26/5841119.aspx

          posted on 2010-10-04 10:33 何克勤 閱讀(359) 評論(0)  編輯  收藏 所屬分類: Algorithm and Data Structure
          主站蜘蛛池模板: 阆中市| 新建县| 鞍山市| 兴和县| 陆川县| 华蓥市| 富阳市| 延长县| 贵阳市| 宜兰市| 广河县| 新丰县| 马公市| 务川| 定襄县| 黄大仙区| 宁陵县| 桐庐县| 五寨县| 辽宁省| 寻乌县| 庆元县| 宁化县| 黔东| 吉首市| 石门县| 唐海县| 镇巴县| 庆云县| 车致| 米易县| 龙游县| 延川县| 安徽省| 镇坪县| 新巴尔虎右旗| 乌海市| 扶沟县| 平安县| 日土县| 柯坪县|