Preface:
什麼是 Skip List?要說清楚這個問題,我們就要先從普通的 Linked List 說起. 考慮我們有一個 Sorted Linked List 如下:
上面鍊表的元素依小到大排序 (ascending), 如果要插入一個元素, 範例代碼如下:
- public boolean insert(int data)
- {
- if(head==null)
- {
- head = new Node(data);
- }
- else
- {
- Node tmp = head;
- if(tmp.data
- {
- while(tmp.next!=null && tmp.next.data
- if(tmp.next!=null && tmp.next.data==data||tmp.data==data) return false;
- else
- {
- Node newNode = new Node(data);
- newNode.next = tmp.next;
- tmp.next = newNode;
- }
- }
- else
- {
- Node newNode = new Node(data);
- newNode.next = head;
- head = newNode;
- }
- }
- return true;
- }
- public Node find(int data)
- {
- if(head!=null)
- {
- Node tmp = head;
- while(tmp!=null)
- {
- if(tmp.data==data) return tmp;
- else if(tmp.data
- else break;
- }
- }
- return null;
- }
- public boolean delete(int data)
- {
- if(head!=null)
- {
- Node tmp = head, prv = null;
- while(tmp!=null)
- {
- if(tmp.data == data)
- {
- if(prv==null) head = tmp.next;
- else
- {
- prv.next = tmp.next;
- }
- return true;
- }
- prv = tmp;
- tmp = tmp.next;
- }
- }
- return false;
- }
Skip List 簡介:
好,下面我們正式開始介紹 Skip List (跳表)。 Skip List 是個概率性數據結構,可以被看作是 BST(二元搜尋數) 的一個變種。跳表是由William Pugh在1990年發明的。它是一種用戶維護有序元素的數據結構. 跳表的構造過程是:
一個跳表,應該具有以下特徵:
接著讓我們用一個跳表來重新構建文章開頭的有序鏈表 (底下是其中一種可能的跳表):
上面的跳表中的 head 是一個陣列, 分別指向各個 Level 的鍊表中第一個元素, 而我們可以透過它在不同 Level 的鍊表中移動; 而相同元素在不同 Level 間也有一個類似的陣列以方便搜尋時在不同 Level 上下移動. 接著假設我們要搜尋數字 "117", 則會按照下面的規則進行搜尋:
搜尋 "117" 過程如下:
搜尋 "21" 過程如下:
底下是搜尋過程的範例代碼:
- public SNode
find(E searchValue) - {
- int curLevel = level;
- SNode
x = header; - while(curLevel>=0 && x!=null)
- {
- while (x.forward[curLevel] != null &&
- x.forward[curLevel].value.compareTo(searchValue) < 0)
- x = x.forward[curLevel];
- System.out.printf("\t[Test] Hold in %s in level(%d)...\n", x.value!=null?x.value:"header", curLevel);
- if(x.forward[curLevel]!=null &&
- x.forward[curLevel].value.equals(searchValue)) return x.forward[curLevel];
- else
- {
- if(curLevel>0) // 還有下一層, 往下一層移動. 此時 x 會停留當前層在小於
的最大 pointer 上. - {
- curLevel--; // 往下層移動
- }
- else return null;
- }
- }
- return null;
- }
From Linked List to Skip List:
現在問題來了, 假設我有一個 ascending order sorted 的 Linked List, 但我想把它轉成 Skip List (跳表) 的話, 該怎麼進行呢? 這個部分我分兩個部分來說明. 首先我們知道 Level 0 層需包含所有元素, 所以不假思索就硬幹了:
- Random rdm = new Random(); // Random 物件稍後會用到
- Iterator
iter = sortedList.iterator(); // 為來源 sorted ascending order Linked List - SNode
zeroLevel = header.forward[0]; // header 為每層的入口陣列, 使用 index 說明第幾層. - HashMap
> nodeMap = new HashMap >(); // 記載某個 Node 與其值的 mapping - while(iter.hasNext())
- {
- E data = iter.next();
- SNode
x = new SNode (MAX_LEVEL, (E)data); // 建立 Skip List 的 SNode. - nodeMap.put(data, x); // 紀錄 SNode mapping, 方便後續取出.
- if(zeroLevel==null)
- {
- //
為 null 說明 為 Level 0 的第一個元素. - header.forward[0] = x;
- zeroLevel = header.forward[0];
- }
- else {
- // 串接
下一個元素為 後將 指向 - zeroLevel.forward[0] = x;
- zeroLevel = zeroLevel.forward[0];
- }
- }
- for(int level=1; level<=MAX_LEVEL; level++)
- {
- SNode
nextLevel = null; - SNode
prevSN = header.forward[level-1]; // 去頭 - if(prevSN==null||prevSN.forward[level-1]==null) continue;
- do
- {
- E data = prevSN.value;
- if(rdm.nextFloat()>P)
- {
- System.out.printf("\t[Test] Level%d: %d\n", level, data);
- if(nextLevel == null)
- {
- //
為 null 說明處理此層的第一個元素. - header.forward[level] = nodeMap.get(data);
- nextLevel = header.forward[level];
- }
- else
- {
- nextLevel.forward[level] = nodeMap.get(data);
- nextLevel = nextLevel.forward[level];
- }
- }
- prevSN = prevSN.forward[level-1];
- if(prevSN==null) break;
- }while(prevSN.forward[level-1]!=null); // 去尾
- // 設定目前最高層的 level 為當前處理的 level.
- if(nextLevel!=null) // nextLevel 不為 null 說明當前層至少有一個元素.
- {
- this.level = level;
- nextLevel = null;
- }
- }
至於 Skip List 的其他操作如 delete, insert 等, 有興趣的可以去下載 SkipList.java. 裡面有完整的實作代碼. 而該範例代碼是改寫自 這裡 的 SkipSet.java
沒有留言:
張貼留言