點贊再看,養成習慣,微信搜索「小大白日志」關注這個搬磚人。
文章不定期同步公眾號,還有各種一線大廠面試原題、我的學習系列筆記。
廣州這邊封閉式管理好久了,今天終於周末可以出去溜溜了

什么是zset
zset是redis中一種有序、不重復的數據類型,每個元素都有一個分值,它可用於實現排行榜單,其底層采用壓縮表ziplist或跳表skiplist的數據結構實現
zset的兩種數據結構
- 壓縮表ziplist
當redis插入第一個元素時,同時滿足以下條件,就會以ziplist創建跳表
- 節點數量<128 (可通過server.zset_max_ziplist_entries設置)
- 節點的長度<64(可通過server.zset-max-zip以上任一個,都會list-value設置)
當選擇用ziplist實現zset后,以后插入的節點若不滿足以上任一個條件,就會轉為skiplist
- 跳表skiplist
跳表的本質是一個多層鏈表,它能快速地查詢、插入、刪除【時間復雜度均為O(logn)】,所以它的查詢速度媲美平衡二叉樹,而且它的數據結構比平衡二叉樹簡單,結構示意圖如下:
特點:
- 跳表的最底層擁有所有的元素
- 跳表每一層都是一個鏈表,除了最底層是原始鏈表,層次逐漸往上可分別划分為一級索引層、二級索引層...
- 跳表插入元素時,會先隨機生成出一個“層次數字”,然后元素會插入到這個層次的所有底層,直到原始鏈表層
- 如果一個元素存在與某個索引層,那么這個元素也會存在於低於它的所有索引下層,如元素在第99索引層,那么由上到下從99索引層直到原始鏈表層都會存在該元素
- 空間換時間,跳表查找變快了,但是要存儲許多索引層,故空間開銷變大了
/**
* 產生節點的高度。使用拋硬幣
*
* @return
*/
private int getRandomLevel() {
//可知,元素的插入層次i從1開始自增,隨機到哪一層的概率就像拋硬幣一樣,都是50%,故i越往后,其概率越小(每次都*0.5)
//第一層概率:0.5,第二層0.5*0.5=0.25,...
int lev = 1;
while (random.nextInt() % 2 == 0) {
lev++;
}
//MAX_LEVEL為跳表的最大層級
return lev > MAX_LEVEL ? MAX_LEVEL : lev;
}
跳表skiplist
-
插入節點
- 插入的時間復雜度為O(logn),每次插入都會先查找到要插入的位置(查找的時間復雜度就已經是【O(logn)】了,找到后直接插入【O(1)】,所以總的為【O(logn)】),刪除也是同理為O(logn)
- 每個節點的插入層次是通過getRandomLevel()隨機出來的,插入層次互不影響
以下模擬節點插入:
-
查找
查找節點時,從高索引層往低索引層查找:
一開始元素在高層從鏈表由前往后查找,直到要查找的目標元素在該層的某兩個相鄰元素之間,就會往下跳到下層的同一個位置,繼續從同一位置向鏈表尾方向遍歷查詢->重復上面的過程,直到查找到目標元素
查找時每一層都跳過部分元素,進而加快了查找效率,以下模擬節點查找:
OK,如果文章哪里有錯誤或不足,歡迎各位留言。
創作不易,各位的「三連」是二少創作的最大動力!我們下期見!