總的來講跳躍鏈表最大的好處就是提升了檢索了的速率,能夠說說是大幅度的提升,相對於單鏈表來講是一種高效率的檢索結構java
跳躍表的結構是:假如底層有10個節點, 那麼底層的上一層理論上就有5個節點,再上一層理論上就有2個或3個節點,再上一層理論上就有1個節點。因此從這裏能夠看出每一層的節點個數爲其下一層的1/2個元素,以此類推。從這裏咱們能夠看到,從插入時咱們只要保證上一層的元素個數爲下一層元素個數的1/2,咱們的跳躍表就能成爲理想的跳躍表。那麼怎麼才能保證咱們插入時上層元素個數是下層元素個數的1/2呢,?很簡單 拋硬幣就能夠解決了,假設元素X要插入跳躍表,最底層是確定要插入X的,那麼次低層要不要插入呢,咱們但願上層元素個數是下層的1/2,那麼咱們有1/2的機率要插入到次低層,這樣就來拋硬幣吧,正面就插入,反面就不插入,次次底層相對於次低層,咱們仍是有1/2的機率插入,那麼就繼續拋硬幣吧 , 以此類推元,素X插入第n層的機率是(1/2)的n次。這樣,咱們能在跳躍表中插入一個元素了。基本的樣子以下圖:git
package skip; public class Node { public Integer value; //插入的數據 public Node left; //分別對應的四個方向的指針 public Node down; public Node right; public Node up; public Node(Integer value) //構造函數 { this.value=value; down=up=right=left=null; } }
package skip; import java.util.Random; public class SkipList { private Node head; //最上面一側的頭結點,這裏使用的是雙鏈表 private Node tail; //最上面一層的尾節點,這裏的頭尾節點是不存儲數據的,數據域全是null private int level; //表中的最高的層數,就是總共的層數 private int size; //插入節點的個數,頭尾節點除外 private Random random; //用來判斷是否須要增長高度的隨機函數 public SkipList() { level = 0; //level默認是0層,就是隻有最下面的一層 head = new Node(null); tail = new Node(null); head.right = tail; //這裏初始化成一個只有一層的雙鏈表 tail.left = head; size = 0; //size初始化爲0 random = new Random(); } //這個函數的做用是找到插入節點的前面一個節點,這裏默認的是將表升序存儲 public Node findFirst(Integer value) { Node p = head; while (true) { //判斷要插入的位置,當沒有查到尾節點而且要插入的數據仍是比前面的大的話,就將節點右移,知道找到合適的位置 //這裏須要注意的是這裏的head表明不必定是最底層的,所以這裏的查找都是從最高層進行查找的,若是知足條件就要向下移動 //直到最底層 while (p.right.value != null && p.right.value <= value) { p = p.right; } //向下移動,直到到達最後一層 if (p.down != null) { p = p.down; } else { //到達最底層跳出便可 break; } } return p; //此時這裏的p就是要插入節點的前面一個節點 } //這是插入函數,這裏先執行插入,而後判斷是否須要增長高度 public void insert(int value) { Node curr = findFirst(value); //先找到插入位置的前面一個節點 Node q = new Node(value); //新建一個插入的節點 //下面執行插入步驟,這個和雙鏈表是同樣的步驟 q.right = curr.right; q.left = curr; curr.right.left = q; curr.right = q; int i = 0; //表示當前節點所在的層數,開始插入的是在下面插入的,因此開始的時候是在0層 //這裏判斷是否須要增長高度,每一層相對域下面來講都有二分之一的機率,也就是說每一層增長的機率是(1/2)^n //通俗的說就是每一層的節點是將會保證是下面一層的1/2 while (random.nextDouble() < 0.5) { if (i >= level) { //若是當前插入的節點所處的層數大於等於最大的層數,那麼就須要增長高度了,由於這裏要保證頭尾節點的高度是最高的 //下面的代碼就是在頭尾節點的上插入新的節點,以此來增長高度 Node p1 = new Node(null); Node p2 = new Node(null); p1.right = p2; p1.down = head; p2.left = p1; p2.down = tail; head.up = p1; //將頭尾節點上移,成爲最頂層的節點,這就是爲何每次插入和查詢的時候都是最上面開始查詢的,由於這裏的head默認的就是從最上面開始的 tail.up = p2; head = p1; tail = p2; level++; //最高層數加一 } while (curr.up == null) { //固然增長高度就是在插入節點上面新插入一個節點,而後將之與插入的節點相連 //既然這裏新插入節點增高了,那麼就須要找到與新插入節點上面的那個節點相鏈接,這裏咱們將新插入節點的前面的同等高度的節點與之相連 curr = curr.left; } curr = curr.up; //經過前面的一個節點找到與之相連的節點 //下面就是建立一個節點插入到插入節點的頭上以此來增長高度,而且這個節點與前面同樣高的節點相連 Node e = new Node(value); e.left = curr; e.right = curr.right; curr.right.left = e; //此時的curr就是與之同等高度的節點 curr.right = e; e.down = q; q.up = e; q = e; //將新插入的節點上移到最上面,由於後面可能還要在這裏增長高度,就是在最上面插入新的如出一轍的節點 i++; //增長當前所處的高度,這裏必定能要記得寫上,若是還要繼續增長的話,須要判讀是否須要增長頭尾節的高度 } size++; //節點加一 } //下面是打印每一層節點的狀況 public void display() { while (level >= 0) { Node p = head; while (p != null) { System.out.print(p.value + "-------->"); p = p.right; } System.out.println(); System.out.println("*****************************"); level--; head = head.down; } } /*在鏈表中查找某個值是否存在,若是存在找到的節點,固然先從最高層開始查找,若是找到了在比這個值小的最後一個值,那麼就順着這個值的下面開始尋找,按照上面的步驟 再次尋找,如過這個值正好等於要找的值,就返回true,形象的來講就是造成一個梯度的感受。注意這裏返回的節點必定是最底層的節點,利於下面的刪除操做 * */ public Node search(int value) { Node p = head; while (true) { /*這裏必定要寫成p.right.value!=null,若是寫成p.right!=null運行可能有錯誤, 由於這裏的尾節點的值爲null,可是它的節點不是空的,若是成這樣的話,那麼節點可能會找到尾節點都沒有找到,此時在判斷value的值就出現錯誤 至關與判斷tail.right.value<=value,這個確定是不行的,由於這個節點不存在,是空的更別說值了 */ //從最高層開始判斷找到比這個小的最後一個值,就是找到一個節點的前面比value小的,後面的節點的值比value大的 while (p.right.value != null && p.right.value <= value) { p = p.right; //若是沒有找到就後移直到找到這個節點 } //若是找到的這個節點不是最底層的話,就向下移動一層,而後循環再次尋找,總之就是從最高層開始,一層一層的尋找 if (p.down != null) { //這個表示上面的循環沒有找到的相等的,那麼就向下移動一層 p = p.down; } else { //若是到了最底層了,這裏的值仍然沒有找到這個值,那麼就表示不存在這個值 if (p.value == value) { //判斷是否存在value相等的值 // System.out.println(p.value + "----->"); return p; //返回節點 } return null; //仍然沒有找到返回null } } } /* 這裏是利用上面的查找函數,找到當前須要刪除的節點,固然這個節點是最底層的節點,而後循環從最底層開始刪除全部的節點 * */ public void delete(int value) { Node temp = search(value); //這裏返回的必須是最底層的節點,由於要從最下面的往上面所有刪除全部層的節點,不然的話可能在某一層上仍然存在這個節點 while (temp != null) { temp.left.right = temp.right; temp.right.left = temp.left; temp = temp.up; //節點上移,繼續刪除上一層的節點 } } public static void main(String args[]) { SkipList skipList = new SkipList(); Random random = new Random(); skipList.insert(33); skipList.insert(44); skipList.insert(11); skipList.insert(10); skipList.insert(22); skipList.insert(22); for (int i = 0; i < 500; i++) { int value = (int) (random.nextDouble() * 1000); skipList.insert(value); // System.out.println(value); } Node p = skipList.search(22); if (p != null) { System.out.println(p.value); } else System.out.println("沒有找到"); skipList.delete(33); skipList.display(); } }