c++實現(xiàn)跳躍表(Skip List)的方法示例
前言
Skip List是一種隨機化的數(shù)據(jù)結(jié)構(gòu),基于并聯(lián)的鏈表,其效率可比擬于二叉查找樹(對于大多數(shù)操作需要O(log n)平均時間)?;旧?,跳躍列表是對有序的鏈表增加上附加的前進鏈接,增加是以隨機化的方式進行的,所以在列表中的查找可以快速的跳過部分列表(因此得名)。所有操作都以對數(shù)隨機化的時間進行。Skip List可以很好解決有序鏈表查找特定值的困難。
跳表是平衡樹的一種替代的數(shù)據(jù)結(jié)構(gòu),但是和紅黑樹不相同的是,跳表對于樹的平衡的實現(xiàn)是基于一種隨機化的算法的,跳躍表使用概率均衡技術(shù)而不是使用強制性均衡,因此,對于插入和刪除結(jié)點比傳統(tǒng)上的平衡樹算法更為簡潔高效。
一個跳表具有以下特征:
1.一個跳表應該有幾個層(level)組成;
2.跳表的第一層包含所有的元素;
3.每一層都是一個有序的鏈表;
4.如果元素x出現(xiàn)在第i層,則所有比i小的層都包含x;
5.第i層的元素通過一個down指針指向下一層擁有相同值的元素;
6.Top指針指向最高層的第一個元素。
下面來研究一下跳表的核心思想: 先從鏈表開始,如果是一個簡單的鏈表,那么我們知道在鏈表中查找一個元素I的話,需要將整個鏈表遍歷一次。
如果是說鏈表是排序的,并且節(jié)點中還存儲了指向前面第二個節(jié)點的指針的話,那么在查找一個節(jié)點時,僅僅需要遍歷N/2個節(jié)點即可。
如上圖所示,是一個即為簡單的跳躍表。傳統(tǒng)意義的單鏈表是一個線性結(jié)構(gòu),向有序的鏈表中插入一個節(jié)點需要O(n)的時間,查找操作需要O(n)的時間。如果我們使用上圖的跳躍表,就可以減少查找所需時間為O(n/2),因為我們可以先通過每個節(jié)點的最上面的指針先進行查找,這樣子就能跳過一半的節(jié)點。比如我們想查找19,首先和6比較,大于6之后,在和9進行比較,然后在和12進行比較......最后比較到21的時候,發(fā)現(xiàn)21大于19,說明查找的點在17和21之間,從這個過程中,我們可以看出,查找的時候跳過了3、7、12等點,因此查找的復雜度為O(n/2)。
當然上面只是最簡單的就是跳躍表,真正的跳表每一個結(jié)點不單單只包含指向下一個結(jié)點的指針,可能包含很多個指向后續(xù)結(jié)點的指針,這樣就可以跳過一些不必要的結(jié)點,從而加快查找、刪除等操作。對于一個鏈表內(nèi)每一個結(jié)點包含多少個指向后續(xù)元素的指針,這個過程是通過一個隨機函數(shù)生成器得到,就是通過隨機生成一個結(jié)點中指向后續(xù)結(jié)點的指針數(shù)目。
通過上面的跳表的很容易設計這樣的數(shù)據(jù)結(jié)構(gòu):
定義每個節(jié)點類型:
typedef struct nodeStructure *node;
typedef struct nodeStructure
{
keyType key; // key值
valueType value; // value值
// 向前指針數(shù)組,根據(jù)該節(jié)點層數(shù)的
// 不同指向不同大小的數(shù)組
node forward[1];
};
上面的每個結(jié)構(gòu)體對應著圖中的每個節(jié)點,如果一個節(jié)點是一層的節(jié)點的話(如7,12等節(jié)點),那么對應的forward將指向一個只含一個元素的數(shù)組,以此類推。
定義跳表數(shù)據(jù)類型:
// 定義跳表數(shù)據(jù)類型
typedef struct listStructure{
int level;
struct nodeStructure * header;
} * list;
先不看代碼先用圖來描述一下Skip List構(gòu)造,插入和刪除的過程:
構(gòu)造Skip List
1、給定一個有序的鏈表。
2、選擇連表中最大和最小的元素,然后從其他元素中按照一定算法(隨機)隨即選出一些元素,將這些元素組成有序鏈表。這個新的鏈表稱為一層,原鏈表稱為其下一層。
3、為剛選出的每個元素添加一個指針域,這個指針指向下一層中值同自己相等的元素。Top指針指向該層首元素
4、重復2、3步,直到不再能選擇出除最大最小元素以外的元素。
插入過程
例子:插入 119, level = 2
如果 K 大于鏈表的層數(shù),則要添加新的層。
例子:插入 119, K = 4
刪除 21
看到這就很清楚了,上面已經(jīng)提到所謂的Skip List是每層從它的下一層按照某種規(guī)律抽出一些元素,它的操作也很簡單,它的操作其實按層來操作鏈表,基本上是從上往下來操作。
具體的實現(xiàn)如下:
定義數(shù)據(jù)結(jié)構(gòu)
//
// skiplist_def.h
// test
//
// Created by 杜國超 on 17/9/24.
// Copyright © 2017年 杜國超. All rights reserved.
//
#ifndef skiplist_def_h
#define skiplist_def_h
#define MAX_LEVEL 8
typedef int KeyType;
typedef int ValueType;
//定義節(jié)點信息數(shù)據(jù)結(jié)構(gòu)
template <typename K,typename V>
struct NodeStructure {
K key;
V value;
NodeStructure* forward[1];
};
//定義跳躍表數(shù)據(jù)結(jié)構(gòu)
template <typename K,typename V>
struct SkipLisStructure{
int level;
NodeStructure<K,V>* header;
};
typedef struct NodeStructure<KeyType,ValueType> NodeType;
typedef struct SkipLisStructure<KeyType,ValueType> ListType;
typedef NodeType* Node;
typedef ListType* List;
#define NEW_LEVEL_NODE(level) (Node)malloc(sizeof(NodeType) + (level) * sizeof(Node))
#endif /* skiplist_def_h */
增刪查操作實現(xiàn)
//
// skiplist.h
// test
//
// Created by 杜國超 on 17/9/24.
// Copyright © 2017年 杜國超. All rights reserved.
//
#ifndef skiplist_h
#define skiplist_h
#include "skiplist_def.h"
class CSkipList{
public:
CSkipList();
~CSkipList();
public:
ValueType* Search(const KeyType& key);
bool Insert(KeyType& key,ValueType& value);
bool Delete(const KeyType& key,ValueType& value);
void FreeList();
private:
int RandomLevel();
private:
List _skipList;
int _size;
};
#endif /* skiplist_h */
//
// skiplist.cpp
// test
//
// Created by 杜國超 on 17/9/24.
// Copyright © 2017年 杜國超. All rights reserved.
//
#include "skiplist_def.h"
#include "skiplist.h"
#include <stdlib.h>
CSkipList::CSkipList(){
_skipList = (List)malloc(sizeof(ListType));
// 設置跳表的層level,初始的層為0層(數(shù)組從0開始)
_skipList->level = 0;
_skipList->header = NEW_LEVEL_NODE(MAX_LEVEL);
// 將header的forward數(shù)組清空
for(int i = 0; i < MAX_LEVEL; ++i)
_skipList->header->forward[i] = NULL;
_size = 0;
}
CSkipList::~CSkipList()
{
FreeList();
}
ValueType* CSkipList::Search(const KeyType& key){
Node node = _skipList->header;
Node indexNode = NULL;
for(int i = _skipList->level - 1; i >= 0; --i){
while((indexNode = node->forward[i]) && (indexNode->forward[i]->key <= key))
{
if (indexNode->key == key)
{
return &(indexNode->value);
}
node = indexNode;
}
}
return NULL;
}
bool CSkipList::Insert(KeyType& key, ValueType& value)
{
Node update[MAX_LEVEL];
int i;
Node node = _skipList->header;
Node indexNode = NULL;
//尋找key所要插入的位置
for(i = _skipList->level - 1; i >= 0; --i){
while((indexNode = node->forward[i]) && (indexNode->forward[i]->key < key))
{
node = indexNode;
}
update[i] = node;
}
node = node->forward[0];
//如果key已經(jīng)存在
if(node->key == key){
node->value = value;
return false;
}else{
//隨機生成新結(jié)點的層數(shù)
int level = RandomLevel();
if(level > _skipList->level){
for (int i = _skipList->level;i < level;++i)
{
update[i] = _skipList->header;
}
_skipList->level = level;
}
//申請新的結(jié)點
Node newNode = NEW_LEVEL_NODE(level);
newNode->key = key;
newNode->value = value;
//調(diào)整forward指針
for(int i = level - 1; i >= 0; --i){
node = update[i];
newNode->forward[i] = node->forward[i];
node->forward[i] = newNode;
}
//更新元素數(shù)目
++_size;
return true;
}
}
bool CSkipList::Delete(const KeyType& key,ValueType& value){
Node update[MAX_LEVEL];
int i;
Node node = _skipList->header;
Node indexNode = NULL;
//尋找key所要插入的位置
for(i = _skipList->level - 1; i >= 0; --i){
while((indexNode = node->forward[i]) && (indexNode->forward[i]->key < key))
{
node = indexNode;
}
update[i] = node;
}
node = node->forward[0];
//結(jié)點不存在
if(node->key != key){
return false;
}else{
value = node->value;
//調(diào)整指針
for(i = 0; i < _skipList->level; ++i){
if(update[i]->forward[i] != node)
break;
update[i]->forward[i] = node->forward[i];
}
//刪除結(jié)點
free(node);
for(int i = _skipList->level - 1; i >= 0; i--){
if(_skipList->header->forward[i]==NULL){
_skipList->level--;
}
}
//更新鏈表元素數(shù)目
--_size;
return true;
}
}
int CSkipList::RandomLevel()
{
int k = 1;
while (rand()%2)
{
k++;
}
k=(k<MAX_LEVEL)?k:MAX_LEVEL;
return k;
}
void CSkipList::FreeList(){
Node p = _skipList->header;
Node q;
while(p != NULL){
q = p->forward[0];
free(p);
p = q;
}
free(p);
free(_skipList);
_size = 0;
}
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對我們的支持。
上一篇:C語言中二級指針的實例詳解
欄 目:C語言
本文標題:c++實現(xiàn)跳躍表(Skip List)的方法示例
本文地址:http://www.jygsgssxh.com/a1/Cyuyan/1155.html
您可能感興趣的文章
- 04-02c語言沒有round函數(shù) round c語言
- 01-10數(shù)據(jù)結(jié)構(gòu)課程設計-用棧實現(xiàn)表達式求值的方法詳解
- 01-10使用OpenGL實現(xiàn)3D立體顯示的程序代碼
- 01-10深入理解C++中常見的關(guān)鍵字含義
- 01-10求斐波那契(Fibonacci)數(shù)列通項的七種實現(xiàn)方法
- 01-10C語言 解決不用+、-、&#215;、&#247;數(shù)字運算符做加法
- 01-10使用C++實現(xiàn)全排列算法的方法詳解
- 01-10c++中inline的用法分析
- 01-10用C++實現(xiàn)DBSCAN聚類算法
- 01-10深入全排列算法及其實現(xiàn)方法


閱讀排行
本欄相關(guān)
- 04-02c語言函數(shù)調(diào)用后清空內(nèi)存 c語言調(diào)用
- 04-02func函數(shù)+在C語言 func函數(shù)在c語言中
- 04-02c語言的正則匹配函數(shù) c語言正則表達
- 04-02c語言用函數(shù)寫分段 用c語言表示分段
- 04-02c語言中對數(shù)函數(shù)的表達式 c語言中對
- 04-02c語言編寫函數(shù)冒泡排序 c語言冒泡排
- 04-02c語言沒有round函數(shù) round c語言
- 04-02c語言分段函數(shù)怎么求 用c語言求分段
- 04-02C語言中怎么打出三角函數(shù) c語言中怎
- 04-02c語言調(diào)用函數(shù)求fibo C語言調(diào)用函數(shù)求
隨機閱讀
- 04-02jquery與jsp,用jquery
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 08-05織夢dedecms什么時候用欄目交叉功能?
- 08-05dedecms(織夢)副欄目數(shù)量限制代碼修改
- 08-05DEDE織夢data目錄下的sessions文件夾有什
- 01-10C#中split用法實例總結(jié)
- 01-11ajax實現(xiàn)頁面的局部加載
- 01-10使用C語言求解撲克牌的順子及n個骰子
- 01-10delphi制作wav文件的方法
- 01-10SublimeText編譯C開發(fā)環(huán)境設置


