國產懸疑《三伏》試玩版今日15點上線 流程1小時
阿新 • • 發佈:2022-03-21
跳錶
跳錶 (Skip List) 是增加了額外的前向指標的連結串列。
- 跳錶隨機地決定連結串列地哪些節點需要增加前向指標、需要增加多少個指標。
- 目的:提高有序連結串列地查詢效率、支援範圍查詢
跳錶的結構
跳錶的查詢路徑:
-
ListNode
的實現:
template<class K, class E> struct ListNode { typedef pair<const K, E> pairType; pairType value; ListNode<K, E>** next; // 指標陣列,存放一系列前向指標 ListNode(const pairType& thePair, int size) : value(thePair) { next = new ListNode<K, E>*[size]; } };
-
SkipList
的類定義:
template<class K, class E, class Compare=less<K>> class SkipList { public: SkipList(int maxSize, float prob); SkipList(int maxSize, float prob, const Compare& x) : cmp(x) { SkipList(maxSize, prob); } ~SkipList(); ListNode<K, E>* insert(const pair<const K, E>& thePair); void erase(const K& theKey); int size() const { return dSize; } pair<const K, E>* find(const K& theKey); E& operator[](const K& theKey); void display() const // 按照key的順序輸出 { for (auto cur = head->next[0]; cur != tail; cur = cur->next[0]) cout << cur->value.first << ' ' << cur->value.second << endl; } private: ListNode<K, E>* search(const K theKey) const; // 查詢第一個不滿足條件的節點 int getLevel() const; int maxLevel; // 最大層數 int level; // 當前層數 float cutOff; // 在計算插入新節點的層數時,迴圈中如果 rand() > curOff 那麼新增一層 int dSize; // 當前元素個數 ListNode<K, E>* head; // 頭節點 ListNode<K, E>* tail; // 尾節點 ListNode<K, E>** last; // 查詢路徑上,每一層路徑節點的前一個點 Compare cmp; };
- 建構函式
template<class K, class E, class Compare> SkipList<K, E, Compare>::SkipList(int maxSize, float prob) { cutOff = RAND_MAX * prob; // 將概率投射到RAND值域上,否則每次rand()都需要對1取模 level = dSize = 0; maxLevel = (int)ceil(logf((float)maxSize) / logf(1 / prob)) - 1; srand((unsigned int)time(nullptr)); // 設定隨機數種子 pair<K, E> thePair; head = new ListNode<K, E>(thePair, maxLevel + 1); tail = new ListNode<K, E>(thePair, 0); last = new ListNode<K, E>*[maxLevel + 1]; for (int i = 0; i <= maxLevel; ++i) head->next[i] = tail; }
- 解構函式
template<class K, class E, class Compare>
SkipList<K, E, Compare>::~SkipList()
{
auto cur = head;
while (cur != tail)
{
auto pre = cur;
cur = cur->next[0];
delete pre;
}
delete tail;
}
-
search
私有函式,作為工具方法使用,時間複雜度為 \(O(log_{\frac{1}{prob}}n)\),最壞為 \(O(n)\)
template<class K, class E, class Compare>
ListNode<K, E>* SkipList<K, E, Compare>::search(const K theKey) const
{
ListNode<K, E>* pre = head;
for (int i = level; i >= 0; --i)
{
while (pre->next[i] != tail && cmp(pre->next[i]->value.first, theKey))
pre = pre->next[i];
last[i] = pre; // 記錄下路徑上每一層的前一個節點
}
return pre->next[0]; // 返回第一個不滿足比較條件的值
}
-
getLeve
私有函式,作為工具方法使用,保證設定層數為 \(i\) 的概率為 \(prob^i\)
template<class K, class E, class Compare>
int SkipList<K, E, Compare>::getLevel() const
{
int res = 0;
// 通過隨機函式和cutOff來設定概率
while (rand() <= cutOff && res < maxLevel)
res++;
return res;
}
-
insert
公有方法,如果節點已存在則修改,否則插入新節點並返回新節點,時間複雜度和search
一樣。
template<class K, class E, class Compare>
ListNode<K, E>* SkipList<K, E, Compare>::insert(const pair<const K, E>& thePair)
{
auto var = search(thePair.first);
// 如果元素存在則修改它的值
if (var->value.first == thePair.first && var != head && var != tail)
{
var->value.second = thePair.second;
return var;
}
// 否則建立一個新節點
int theLevel = getLevel();
if (theLevel > level)
{
theLevel = ++level;
last[theLevel] = tail;
}
auto newNode = new ListNode<K, E>(thePair, theLevel + 1);
for (int i = 0; i <= theLevel; ++i)
{
newNode->next[i] = last[i]->next[i];
last[i]->next[i] = newNode;
}
dSize++;
return newNode;
}
-
erase
函式,刪除給定key的節點,時間複雜度和search
相同
template<class K, class E, class Compare>
void SkipList<K, E, Compare>::erase(const K& theKey)
{
auto var = search(theKey);
if (var->value.first != theKey) return;
for (int i = 0; i <= level && last[i]->next[i] == var; ++i)
last[i]->next[i] = var->next[i];
while (level && head->next[level] == tail)
--level;
delete var;
dSize--;
}
-
find
函式,提供給使用者查詢相關值,時間複雜度和search
相同
template<class K, class E, class Compare>
pair<const K, E>* SkipList<K, E, Compare>::find(const K& theKey)
{
auto var = search(theKey);
if (var->value.first == theKey && var != head && var != tail)
return &var->value;
return nullptr;
}
- 過載
[]
,提供的操作類似於 map
template<class K, class E, class Compare>
E& SkipList<K, E, Compare>::operator[](const K& theKey)
{
auto var = search(theKey);
if (var->value.first == theKey && var != head && var != tail)
return var->value.second;
pair<K, E> thePair;
thePair.first = theKey;
return insert(thePair)->value.second;
}