每次初始化時初始化最初的節點,也就是最左邊的節點,它的高度就是跳表最大高度,初始每個位置指標指向null。
插入元素時首先判斷該元素在集合中是否存在,從最初節點的最高位置開始尋找,向前的指標鍵值比要找的值大就向下移動,小就向前移動。如果不存在就插入,首先用隨機函式每次隨機乙個數0或1,出現一次1就停止,出現0的個數就是層數。然後從初始節點的對應層開始,如果前乙個節點值大,就生成乙個node,令初始節點層指標指向node,node向前指向本來前面的值,如果前乙個節點值小就向下移動然後繼續判斷,直到走完第一層,對應節點的所有向前指標都已經建立完畢了。
刪除時只需要找到該節點該層誰指向它,把指標指向node前面的node即可。
跳表的node:
class skiplistnode
}
跳表list的字段和構造方法:
class skiplist
}
跳表的add方法:
//新增元素
public void add(integer newvalue)
while(level > maxlevel)
//建立node,確定head為查詢時的起始node
skiplistnode newnode = new skiplistnode(newvalue);
skiplistnode current = head;
//遍歷的時候每次要麼向前走,要麼向下走,按照層數遍歷
//每一層都要建立新node,然後連線前後指標
do while(level-- > 0);}}
查詢方法findnext:
//找到要找的目標值e在這一層level裡剛剛大於e的node,current是尋找的起始位置
private skiplistnode findnext(integer e, skiplistnode current, int level)
//如果本節點是8,前乙個節點是10,要找的值是12就會繼續向前找
//更新current和next
//每次如果前乙個節點比要找的值小就前進,否則找下一層
current = next;
next = current.nextnodes.get(level);
}return current;
}
contains方法:
public boolean contains(integer value)
//找到e對應的節點
private skiplistnode find(integer e)
delete方法:
public void delete(integer deletevalue)
}while(level-- > 0);}}
跳表的迭代器(遍歷結果一定是從小到大的):
class skiplistiterator implements iterator
@override
public boolean hasnext()
@override
public integer next()
}
在組織score時redis採用跳表,而從member到score的對映redis使用字典來儲存。
在redis中每一層的晉公升概率是25%,它是一種更扁平化的跳表,在單個層上遍歷的節點個數就會稍多一些。
在調整元素權重時,redis採用對該節點先刪後加的方式來進行。
如果權重都相同redis還會比較value值,使redis中的跳表有序。
redis計算元素排名rank時,是對跳表功能的一種加強,對於每個元素都有它的字段rank值,跳表在變化時會更新這個值。
在redis中跳躍表是有序集合sorted set的底層實現之一。跳躍表通過在每個節點中維持多個指向其他節點的指標,從而達到快速訪問節點的目的。支援平均ologn、最壞on複雜度的節點查詢,還可以通過順序性操作來批量處理節點。
redis的鍊錶由兩種結構組成,乙個是結點zskiplistnode,乙個是表zskiplist。下圖為乙個跳表例子:
最左邊就是乙個zskiplist,它包括header(指向跳躍表的表頭節點)、tail(指向跳躍表的表尾節點)、level(當前跳躍表的最大層數)、length(跳躍表的長度,也就是資料節點的數量)。
右側是4個zskiplistnode,它有幾個屬性:
1、層level,也就是l1/l2/../l4等,每個層都有兩個屬性,分別是指標(指向表尾方向的其他節點)、跨度(記錄跨度方向兩點的距離),當從表頭向表尾遍歷跳表時,就會借助前進指標。
2、後退指標bw,它用來指向當前節點的前乙個節點,在從表尾向表頭遍歷跳表時,就會借助後退指標。
3、分值:每個節點中的1.0、2.0、3.0。節點按照分值從小到大排列。
4、成員物件obj:o1、o2和o3,它是節點儲存的值。
zskiplistnode的定義如下:
typedef struct zskiplistnode level;
//後退指標
struct zskiplistnode *backward;
//分值
double score;
//成員物件
robj *obj;
} zskiplistnode;
每次生成乙個新跳躍表節點的時候,程式都會自動生成乙個介於1到32之間的值作為level陣列的大小,這個大小就是層的高度,每一層的晉公升概率是25%,這是乙個更偏於扁平的跳表。
span跨度這個欄位是為了計算某個元素的排位rank使用的,在查詢某個節點的過程中,將沿途訪問的所有層的跨度都累加起來,得到的結果就是目標節點在跳躍表中的排位。
obj是乙個指向sds的指標,在同乙個跳表中,各節點儲存的物件obj必須是唯一的,而分值score可以是相同的,分值相同的節點會按照物件obj的字典序大小來進行排序。
zskiplist的定義如下:
typedef struct zskiplist zskiplist;
Redis的跳躍表底層實現
跳躍表是一種有序的資料結構,主要用在zset 有序集合 和集群節點的內部資料結構。在大部分情況下,跳躍表的效率可以和平衡樹相媲美,並且因為跳躍表的實現比平衡樹要來得更為簡單,所以有不少程式都使用跳躍表來代替平衡樹。注意mysql的底層採用的是b 樹實現。跳躍表的實現 redis的跳躍表由redis....
Redis底層之鍊錶
redis底層鍊錶節點使用listnode來實現。每個listnode節點包含三個成員,乙個prev指標指向前乙個listnode,乙個next指標指向下乙個節點。乙個value指標指向任何型別的值。多個listnode通過prev和next指標組成雙端鍊錶。通過乙個list比較方便地來管理產生的l...
Redis底層資料結構(6)跳躍表
當乙個有序集合包含的元素數量多,又或者有序集合中的元素的成員是比較長的字串時,redis就會使用跳躍表作為有序集合鍵的底層實現。簡介如果我們要實現按照成績對學生進行排名,可以選擇陣列 鍊錶 平衡樹或紅黑樹來實現,陣列的插入和刪除效率低,鍊錶查詢的效率低,平衡樹或紅黑樹雖然效率高但是實現複雜。跳躍表 ...