跳表介紹和實現

想慢慢的給大家自然的引入跳表。

?

想想,我們

1)在有序數列里搜索一個數

2)或者把一個數插入到正確的位置

都怎么做?

很簡單吧

對于第一個操作,我們可以一個一個比較,在數組中我們可以二分,這樣比鏈表快

對于第二個操作,二分也沒什么用,因為找到位置還要在數組中一個一個挪位置,時間復雜度依舊是o(n)。

那我們怎么發明一個查找插入都比較快的結構呢?

?

?

?

可以打一些標記:

這樣我們把標記連起來,搜索一個數時先從標記開始搜起下一個標記比本身大的話就往下走,因為再往前就肯定不符合要求了。

比如我們要搜索18:

因為一次可以跨越好多數呀,自然快了一些。

既然可以打標記,我們可以改進一下,選出一些數來再打一層標記:

這樣我們搜索20是這樣的:

最終我們可以打好多層標記,我們從最高層開始搜索,一次可以跳過大量的數(依舊是右邊大了就往下走)。

比如搜索26:

最好的情況,就是每一層的標記都減少一半,這樣到了頂層往下搜索,其實和二分就沒什么兩樣,我們最底層用鏈表串起來,插入一個元素也不需要移動元素,所謂跳表就完成了一大半了。

?

現在的問題是,我們對于一個新數,到底應該給它打幾層標記呢?

(剛開始一個數都沒有,所以解決了這個問題,我們一直用這個策略更新即可)

答案是。。。。。投硬幣,全看臉。

我其實有點驚訝,我以為會有某些很強的和數學相關的算法,可以保證一個很好的搜索效率,是我想多了。

我們對于一個新數字,有一半概率可以打一層標記,有一半概率不可以打。

對于打了一層標記的數,我們依舊是這個方法,它有一半概率再向上打一層標記,依次循環。

所以每一層能到達的概率都少一半。

各層的節點數量竟然就可以比較好的維護在很好的效率上(最完美的就是達到了二分的效果)

?

再分析一下,其實對于同一個數字:

等等。。

其實沒必要全都用指針,因為我們知道,通過指針找到一個數可比下標慢多了。

所以同一個數字的所有標記,沒必要再用指針,效率低還不好維護,用一個list保存即可。

這樣,我們就設計出來一個數字的所有標記組成的結構:

	public static class SkipListNode {public Integer value;//本身的值public ArrayList<SkipListNode> nextNodes;
//指向下一個元素的結點組成的數組,長度全看臉。public SkipListNode(Integer value) {this.value = value;nextNodes = new ArrayList<SkipListNode>();}}

將integer比較的操作封裝一下:

		private boolean lessThan(Integer a, Integer b) {return a.compareTo(b) < 0;}private boolean equalTo(Integer a, Integer b) {return a.compareTo(b) == 0;}

找到在本層應該往下拐的結點:

		// Returns the node at a given level with highest value less than eprivate SkipListNode findNext(Integer e, SkipListNode current, int level) {SkipListNode next = current.nextNodes.get(level);while (next != null) {Integer value = next.value;if (lessThan(e, value)) { // e < valuebreak;}current = next;next = current.nextNodes.get(level);}return current;}

這樣我們就寫一個一層層往下找的方法,并且封裝成find(Integer e)的形式:

		// Returns the skiplist node with greatest value <= eprivate SkipListNode find(Integer e) {return find(e, head, maxLevel);}// Returns the skiplist node with greatest value <= e// Starts at node start and levelprivate SkipListNode find(Integer e, SkipListNode current, int level) {do {current = findNext(e, current, level);} while (level-- > 0);return current;}

剛才的方法是找到最大的小于等于目標的值,如果找到的值等于目標,跳表中就存在這個目標。否則不存在。

		public boolean contains(Integer value) {SkipListNode node = find(value);return node != null && node.value != null && equalTo(node.value, value);}

我們現在可以實現加入一個新點了,要注意把每層的標記打好:

		public void add(Integer newValue) {if (!contains(newValue)) {size++;int level = 0;while (Math.random() < PROBABILITY) {level++;//能有幾層全看臉}while (level > maxLevel) {//大于當前最大層數head.nextNodes.add(null);//直接連系統最大maxLevel++;}SkipListNode newNode = new SkipListNode(newValue);SkipListNode current = head;//前一個結點,也就是說目標應插current之后do {//每一層往下走之前就可以設置這一層的標記了,就是鏈表插入一個新節點current = findNext(newValue, current, level);newNode.nextNodes.add(0, current.nextNodes.get(level));current.nextNodes.set(level, newNode);} while (level-- > 0);}}

刪除也是一樣的

		public void delete(Integer deleteValue) {if (contains(deleteValue)) {SkipListNode deleteNode = find(deleteValue);size--;int level = maxLevel;SkipListNode current = head;do {//就是一個鏈表刪除節點的操作current = findNext(deleteNode.value, current, level);if (deleteNode.nextNodes.size() > level) {current.nextNodes.set(level, deleteNode.nextNodes.get(level));}} while (level-- > 0);}}

作為一個容器,Iterator那是必須有的吧,里面肯定有hasNext和next吧?

	public static class SkipListIterator implements Iterator<Integer> {SkipList list;SkipListNode current;public SkipListIterator(SkipList list) {this.list = list;this.current = list.getHead();}public boolean hasNext() {return current.nextNodes.get(0) != null;}public Integer next() {current = current.nextNodes.get(0);return current.value;}}

這個跳表我們就實現完了。

現實工作中呢,我們一般不會讓它到無限多層,萬一有一個數它人氣爆炸隨機數沖到了一萬層呢?

所以包括redis在內的一些跳表實現,都是規定了一個最大層數的。

別的好像也沒什么了。

最后貼出所有代碼。

import java.util.ArrayList;
import java.util.Iterator;public SkipListDemo {public static class SkipListNode {public Integer value;public ArrayList<SkipListNode> nextNodes;public SkipListNode(Integer value) {this.value = value;nextNodes = new ArrayList<SkipListNode>();}}public static class SkipListIterator implements Iterator<Integer> {SkipList list;SkipListNode current;public SkipListIterator(SkipList list) {this.list = list;this.current = list.getHead();}public boolean hasNext() {return current.nextNodes.get(0) != null;}public Integer next() {current = current.nextNodes.get(0);return current.value;}}public static class SkipList {private SkipListNode head;private int maxLevel;private int size;private static final double PROBABILITY = 0.5;public SkipList() {size = 0;maxLevel = 0;head = new SkipListNode(null);head.nextNodes.add(null);}public SkipListNode getHead() {return head;}public void add(Integer newValue) {if (!contains(newValue)) {size++;int level = 0;while (Math.random() < PROBABILITY) {level++;}while (level > maxLevel) {head.nextNodes.add(null);maxLevel++;}SkipListNode newNode = new SkipListNode(newValue);SkipListNode current = head;do {current = findNext(newValue, current, level);newNode.nextNodes.add(0, current.nextNodes.get(level));current.nextNodes.set(level, newNode);} while (level-- > 0);}}public void delete(Integer deleteValue) {if (contains(deleteValue)) {SkipListNode deleteNode = find(deleteValue);size--;int level = maxLevel;SkipListNode current = head;do {current = findNext(deleteNode.value, current, level);if (deleteNode.nextNodes.size() > level) {current.nextNodes.set(level, deleteNode.nextNodes.get(level));}} while (level-- > 0);}}// Returns the skiplist node with greatest value <= eprivate SkipListNode find(Integer e) {return find(e, head, maxLevel);}// Returns the skiplist node with greatest value <= e// Starts at node start and levelprivate SkipListNode find(Integer e, SkipListNode current, int level) {do {current = findNext(e, current, level);} while (level-- > 0);return current;}// Returns the node at a given level with highest value less than eprivate SkipListNode findNext(Integer e, SkipListNode current, int level) {SkipListNode next = current.nextNodes.get(level);while (next != null) {Integer value = next.value;if (lessThan(e, value)) { // e < valuebreak;}current = next;next = current.nextNodes.get(level);}return current;}public int size() {return size;}public boolean contains(Integer value) {SkipListNode node = find(value);return node != null && node.value != null && equalTo(node.value, value);}public Iterator<Integer> iterator() {return new SkipListIterator(this);}/******************************************************************************* Utility Functions *******************************************************************************/private boolean lessThan(Integer a, Integer b) {return a.compareTo(b) < 0;}private boolean equalTo(Integer a, Integer b) {return a.compareTo(b) == 0;}}public static void main(String[] args) {}}

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/445409.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/445409.shtml
英文地址,請注明出處:http://en.pswp.cn/news/445409.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

機器學習知識總結系列- 基本概念(1-0)

文章目錄目錄1. 機器學習的定義2. 機器學習的分類2.1根據是否在人類監督下進行訓練監督學習非監督學習半監督學習強化學習2.2根據是否可以動態漸進的學習在線學習批量學習2.3根據是否在訓練數據過程中進行模式識別實例學習基于模型的學習3. 機器學習中的一些常見名詞4. 機器學習…

劍指offer(刷題21-30)--c++,Python版本

文章目錄目錄第 21題&#xff1a;解題思路&#xff1a;代碼實現&#xff1a;cpython第22 題&#xff1a;解題思路&#xff1a;代碼實現&#xff1a;cpython第23 題&#xff1a;解題思路&#xff1a;代碼實現&#xff1a;cpython第24 題&#xff1a;解題思路&#xff1a;代碼實現…

redis——對象

剛寫了redis主要的數據結構&#xff1a; 動態字符串、雙端鏈表、字典、壓縮列表、整數集合、跳表等 redis肯定不能直接使用這些數據結構來實現數據庫&#xff0c;它用這些數據庫建立了一個對象系統&#xff0c;包含&#xff1a; 字符串對象、列表對象、哈希對象、集合對象、…

劍指offer(刷題31-40)--c++,Python版本

文章目錄目錄第31 題&#xff1a;解題思路&#xff1a;代碼實現&#xff1a;cpython第32題&#xff1a;解題思路&#xff1a;代碼實現&#xff1a;cpython第33題&#xff1a;解題思路&#xff1a;代碼實現&#xff1a;cpython第34題&#xff1a;解題思路&#xff1a;代碼實現&a…

redis——數據庫

redis服務器將所有數據庫都保存在redis/redisServer中&#xff0c;數組db存放所有數據庫&#xff0c;每一項是一個redisdb結構。dbnum代表數據庫數量。 客戶端有一個指針指向當前數據庫&#xff0c;可以切換&#xff0c;也就是移動指針。 鍵空間 現在稍微介紹一下redisdb結構…

劍指offer(刷題41-50)--c++,Python版本

文章目錄目錄第41題&#xff1a;解題思路&#xff1a;代碼實現&#xff1a;cpython第42題&#xff1a;解題思路&#xff1a;代碼實現&#xff1a;cpython第43題&#xff1a;解題思路&#xff1a;代碼實現&#xff1a;cpython第44題&#xff1a;解題思路&#xff1a;代碼實現&am…

redis——持久化

因為redis是內存數據庫&#xff0c;他把數據都存在內存里&#xff0c;所以要想辦法實現持久化功能。 RDB RDB持久化可以手動執行&#xff0c;也可以配置定期執行&#xff0c;可以把某個時間的數據狀態保存到RDB文件中&#xff0c;反之&#xff0c;我們可以用RDB文件還原數據庫…

redis原理總結

數據結構&#xff08;字典、鏈表、字符串&#xff09; 數據結構&#xff08;整數集合&#xff0c;壓縮列表&#xff09; 數據結構&#xff08;跳表介紹和手撕&#xff09; LRU介紹和實現 對象&#xff08;字符串對象、列表對象、哈希對象、集合對象、有序集合總結&#xff…

劍指offer(刷題51-60)--c++,Python版本

文章目錄目錄第51題&#xff1a;解題思路&#xff1a;代碼實現&#xff1a;cpython第52題&#xff1a;解題思路&#xff1a;代碼實現&#xff1a;cpython第53題&#xff1a;解題思路&#xff1a;代碼實現&#xff1a;cpython第54題&#xff1a;解題思路&#xff1a;代碼實現&am…

2017第一屆河北省大學生程序設計競賽題解

超級密碼 小明今年9歲了&#xff0c;最近迷上了設計密碼&#xff01;今天&#xff0c;他又設計了一套他認為很復雜的密碼&#xff0c;并且稱之為“超級密碼”. 說實話&#xff0c;這套所謂的“超級密碼”其實并不難&#xff1a;對于一個給定的字符串&#xff0c;你只要提取其中…

劍指offer(刷題61-65)--c++,Python版本

文章目錄目錄第61題&#xff1a;解題思路&#xff1a;代碼實現&#xff1a;cpython第62題&#xff1a;解題思路&#xff1a;代碼實現&#xff1a;cpython第63題&#xff1a;解題思路&#xff1a;代碼實現&#xff1a;cpython第64題&#xff1a;解題思路&#xff1a;代碼實現&am…

2018第二屆河北省大學生程序設計競賽題解

icebound的賬單 題目描述 icebound從小就有記賬的習慣。又到了月末icebound統計資金狀況的時候。icebound每個月除了不停的揮霍以外&#xff0c;有時他會良心發現&#xff0c;勤工儉學&#xff0c;因此會有一些微薄的收入。然而icebound數學不好&#xff0c;需要你來幫助他統計…

大數的四則運算(加法、減法、乘法、除法)

大數的四則運算&#xff08;加法、減法、乘法、除法&#xff09; 前言&#xff1a; 在計算機中數字表示的范圍是有限制的&#xff0c;比如我們熟知的 int、float、double 等數據類型所能表示的范圍都是有限的&#xff0c;如果我們要對位數達到幾十位、幾百位、上千位的大整數進…

數組基操三連(1)

題目&#xff1a; 給定一個數組arr&#xff0c;求出需要排序的最短子數組長度 要求&#xff1a; 時間o(n),空間o(1) 思路&#xff1a; 有序的數組中&#xff0c;任意一個數字&#xff0c;一定小于左邊的數大于右邊的數。 我們找到的需要排序的子數組&#xff0c;顯然是比右邊…

IT互聯網公司的筆試的輸入輸出- c++ python

文章目錄目錄c方式1&#xff1a;方式2&#xff1a;Python方式1&#xff1a;方式2&#xff1a;方式3&#xff1a;目錄 c 方式1&#xff1a; 第一種情況&#xff1a;輸入n個數&#xff0c;存放在數組中 #include <iostream> #include <vector> using namespace st…

隨機過程1

隨機過程1概述1.參考書目2.主要內容3.概率論--基本概念回顧3.1對“不確定性”的認識3.2 應對“不確定性”應該怎么做3.3隨機變量&#xff08;Random Variable&#xff09;3.4分布函數&#xff08;Distribution Function&#xff09;3.5概率密度&#xff08;Density&#xff09;…

數組基操三連(4)

題目一 給定一個長度為N的整型數組arr&#xff0c;其中有N個互不相等的自然數1~N 請實現arr的排序 但是不要把下標0~N-1位置上的數值通過直接賦值的方式替換成1~N。 要求&#xff1a;時間復雜度為O(N)&#xff0c;額外空間復雜度為O(1)。 思路&#xff1a;從左向右檢查&…