1.動態的資料結構
之前一次去實習筆試的時候,有一題筆試題是這樣的,設計乙個佇列,使得它在高強度的插入和刪除下仍然具有較好的訪問速度。當時想,陣列雖然好,訪問的時候是o(1)但是一旦涉及到刪除和新增元素的時候,效能就下來了。
由此想到stl中的vector,往裡面新增元素是是新增在末尾,一旦超過了其記憶體大小,在分配一塊更大的記憶體(原來的兩倍)然後將舊元素拷貝到新的記憶體空間去,這樣雖然在拷貝的時候時間代價很高,但是平均下來,每個元素的訪問代價還是o(1)。比如現在vector裡面有4個元素,當我們增加第5個元素的時候,如果忽略分配記憶體用時,複製4個元素用時4個單位,之前對這4個元素存的時候如果向他們收取2個代價,乙個用於儲存,兩個用於將來的拷貝移動,那麼平攤下來,每個元素的訪問代價始終未常數,這就是平攤分析。
既然陣列適合存放靜態資料,那麼就用鍊錶吧,當時我就把乙個普通的鍊錶寫上去了。結果顯然達不到要求,後來在學習的時候發現,其實紅黑樹挺適合解決這個問題的,但是紅黑樹的維護比較複雜,比如插入元素後的旋轉,寫的不多還真比較難調出來。
於是乎總結了一下一些動態的資料結構
這幾種在資料改變時候都能保證良好的搜尋速度,但是如果考慮實現難度的話,跳躍表是最簡單的
2. 跳躍表原理
跳躍表是怎麼來的呢?
一般的鍊錶查詢乙個元素的時候就是挨個查詢,那麼怎麼加速它的查詢呢?
另外新建乙個鍊錶,其中包含原煉表中的一些元素,先到這個鍊錶裡面查大概的位置,然後再到原鍊錶裡面去查具體的位置。怎麼樣更快呢?
再多建一條鍊錶,在之前鍊錶的基礎之上在隨機選擇某些元素。
迴圈就得到了跳躍表的思想了
當向鍊錶中插入元素的時候,以1/2的概率把它插入到上面一條煉表裡去,這樣當元素即使很多的時候,也能保證上一條鍊錶是下一條長度的一半,每加一條鍊錶,訪問速度就提高一倍,這樣理論上每個元素的訪問速度就達到了logn了。
3. 實際效能
ok,說了這麼多它的好,看看他的實際效能,以插入10w隨機數為例
template void linklist::sl_insert(t val)
if(p->down==null)
break;
stk->pushin(p);
p=p->down;
q=p->next;
} oldnode=new node(val);
p->next=oldnode;
oldnode->next=q;
while(1)
else
oldnode=newnode;
} else
break;
} stk->clear();
}
1.普通的插入方法耗時199400ms
2.跳躍表耗時328ms,加速是很明顯的。
4. 時間複雜度和空間複雜度分析
普通的鍊錶在查詢的時候耗時o(n),總體耗時o(n^2),空間複雜度o(n)
跳躍表查詢耗時ologn),總體耗時o(n*logn),空間複雜度由等比數列求和得到o(2*n)
Redis06 底層 跳躍鍊錶skiplist
每次初始化時初始化最初的節點,也就是最左邊的節點,它的高度就是跳表最大高度,初始每個位置指標指向null。插入元素時首先判斷該元素在集合中是否存在,從最初節點的最高位置開始尋找,向前的指標鍵值比要找的值大就向下移動,小就向前移動。如果不存在就插入,首先用隨機函式每次隨機乙個數0或1,出現一次1就停止...
Redis的跳躍表
typedef struct zskiplistnode level 層 層高1 32隨機,一般層數量越多,訪問其他節點速度越快 struct zskiplistnode backward 後退指標 只能退至前乙個節點 double score 分值 所有節點按分值從小到大排序 robj obj 成...
跳躍表是什麼
加入我們要開發乙個遊戲裡面類似拍賣行的搜尋功能,我們要支援輸入道具名稱的精準查詢和不輸入名稱的全量查詢.拍賣行商品數量幾十萬件,對應資料庫商品表的幾十萬條記錄,按照商品名稱精準查詢好辦,可以直接衝資料庫查出來,如果沒有商品名稱的全量查詢怎麼辦?總不能把資料庫所有記錄查出來,還要支援不同欄位的排序.拍...