欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

c++實(shí)現(xiàn)跳躍表(Skip List)的方法示例

 更新時(shí)間:2017年09月26日 10:36:28   作者:D_Guco  
跳表(skiplist)是一個(gè)非常優(yōu)秀的數(shù)據(jù)結(jié)構(gòu),實(shí)現(xiàn)簡(jiǎn)單,插入、刪除、查找的復(fù)雜度均為O(logN),下面這篇文章主要介紹了c++實(shí)現(xiàn)跳躍表(Skip List)的相關(guān)資料,需要的朋友可以參考借鑒,下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧。

前言

Skip List是一種隨機(jī)化的數(shù)據(jù)結(jié)構(gòu),基于并聯(lián)的鏈表,其效率可比擬于二叉查找樹(對(duì)于大多數(shù)操作需要O(log n)平均時(shí)間)?;旧?,跳躍列表是對(duì)有序的鏈表增加上附加的前進(jìn)鏈接,增加是以隨機(jī)化的方式進(jìn)行的,所以在列表中的查找可以快速的跳過部分列表(因此得名)。所有操作都以對(duì)數(shù)隨機(jī)化的時(shí)間進(jìn)行。Skip List可以很好解決有序鏈表查找特定值的困難。

跳表是平衡樹的一種替代的數(shù)據(jù)結(jié)構(gòu),但是和紅黑樹不相同的是,跳表對(duì)于樹的平衡的實(shí)現(xiàn)是基于一種隨機(jī)化的算法的,跳躍表使用概率均衡技術(shù)而不是使用強(qiáng)制性均衡,因此,對(duì)于插入和刪除結(jié)點(diǎn)比傳統(tǒng)上的平衡樹算法更為簡(jiǎn)潔高效。

一個(gè)跳表具有以下特征:

      1.一個(gè)跳表應(yīng)該有幾個(gè)層(level)組成;

      2.跳表的第一層包含所有的元素;

      3.每一層都是一個(gè)有序的鏈表;

      4.如果元素x出現(xiàn)在第i層,則所有比i小的層都包含x;

      5.第i層的元素通過一個(gè)down指針指向下一層擁有相同值的元素;

      6.Top指針指向最高層的第一個(gè)元素。

下面來研究一下跳表的核心思想: 先從鏈表開始,如果是一個(gè)簡(jiǎn)單的鏈表,那么我們知道在鏈表中查找一個(gè)元素I的話,需要將整個(gè)鏈表遍歷一次。

如果是說鏈表是排序的,并且節(jié)點(diǎn)中還存儲(chǔ)了指向前面第二個(gè)節(jié)點(diǎn)的指針的話,那么在查找一個(gè)節(jié)點(diǎn)時(shí),僅僅需要遍歷N/2個(gè)節(jié)點(diǎn)即可。

如上圖所示,是一個(gè)即為簡(jiǎn)單的跳躍表。傳統(tǒng)意義的單鏈表是一個(gè)線性結(jié)構(gòu),向有序的鏈表中插入一個(gè)節(jié)點(diǎn)需要O(n)的時(shí)間,查找操作需要O(n)的時(shí)間。如果我們使用上圖的跳躍表,就可以減少查找所需時(shí)間為O(n/2),因?yàn)槲覀兛梢韵韧ㄟ^每個(gè)節(jié)點(diǎn)的最上面的指針先進(jìn)行查找,這樣子就能跳過一半的節(jié)點(diǎn)。比如我們想查找19,首先和6比較,大于6之后,在和9進(jìn)行比較,然后在和12進(jìn)行比較......最后比較到21的時(shí)候,發(fā)現(xiàn)21大于19,說明查找的點(diǎn)在17和21之間,從這個(gè)過程中,我們可以看出,查找的時(shí)候跳過了3、7、12等點(diǎn),因此查找的復(fù)雜度為O(n/2)。

當(dāng)然上面只是最簡(jiǎn)單的就是跳躍表,真正的跳表每一個(gè)結(jié)點(diǎn)不單單只包含指向下一個(gè)結(jié)點(diǎn)的指針,可能包含很多個(gè)指向后續(xù)結(jié)點(diǎn)的指針,這樣就可以跳過一些不必要的結(jié)點(diǎn),從而加快查找、刪除等操作。對(duì)于一個(gè)鏈表內(nèi)每一個(gè)結(jié)點(diǎn)包含多少個(gè)指向后續(xù)元素的指針,這個(gè)過程是通過一個(gè)隨機(jī)函數(shù)生成器得到,就是通過隨機(jī)生成一個(gè)結(jié)點(diǎn)中指向后續(xù)結(jié)點(diǎn)的指針數(shù)目。

通過上面的跳表的很容易設(shè)計(jì)這樣的數(shù)據(jù)結(jié)構(gòu):

定義每個(gè)節(jié)點(diǎn)類型:

typedef struct nodeStructure *node;
typedef struct nodeStructure
{
 keyType key; // key值
 valueType value; // value值
 // 向前指針數(shù)組,根據(jù)該節(jié)點(diǎn)層數(shù)的
 // 不同指向不同大小的數(shù)組
 node forward[1]; 
};

上面的每個(gè)結(jié)構(gòu)體對(duì)應(yīng)著圖中的每個(gè)節(jié)點(diǎn),如果一個(gè)節(jié)點(diǎn)是一層的節(jié)點(diǎn)的話(如7,12等節(jié)點(diǎn)),那么對(duì)應(yīng)的forward將指向一個(gè)只含一個(gè)元素的數(shù)組,以此類推。

定義跳表數(shù)據(jù)類型:

// 定義跳表數(shù)據(jù)類型
typedef struct listStructure{
 int level; 
 struct nodeStructure * header;
} * list; 

先不看代碼先用圖來描述一下Skip List構(gòu)造,插入和刪除的過程:

構(gòu)造Skip List

       1、給定一個(gè)有序的鏈表。

       2、選擇連表中最大和最小的元素,然后從其他元素中按照一定算法(隨機(jī))隨即選出一些元素,將這些元素組成有序鏈表。這個(gè)新的鏈表稱為一層,原鏈表稱為其下一層。

       3、為剛選出的每個(gè)元素添加一個(gè)指針域,這個(gè)指針指向下一層中值同自己相等的元素。Top指針指向該層首元素

       4、重復(fù)2、3步,直到不再能選擇出除最大最小元素以外的元素。

插入過程

例子:插入 119, level = 2

如果 K 大于鏈表的層數(shù),則要添加新的層。

例子:插入 119, K = 4


刪除 21

看到這就很清楚了,上面已經(jīng)提到所謂的Skip List是每層從它的下一層按照某種規(guī)律抽出一些元素,它的操作也很簡(jiǎn)單,它的操作其實(shí)按層來操作鏈表,基本上是從上往下來操作。

具體的實(shí)現(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é)點(diǎn)信息數(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 */

增刪查操作實(shí)現(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));
 // 設(shè)置跳表的層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{
  //隨機(jī)生成新結(jié)點(diǎn)的層數(shù)
  int level = RandomLevel();
  if(level > _skipList->level){
   for (int i = _skipList->level;i < level;++i)
   {
    update[i] = _skipList->header;
   }
   _skipList->level = level;
  }
  
  //申請(qǐng)新的結(jié)點(diǎn)
  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;
  }
 
  //更新元素?cái)?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é)點(diǎn)不存在
 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é)點(diǎn)
  free(node);
  for(int i = _skipList->level - 1; i >= 0; i--){
   if(_skipList->header->forward[i]==NULL){
    _skipList->level--;
   }
  }
  //更新鏈表元素?cái)?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é)

以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。

相關(guān)文章

  • CFile與CStdioFile的文件讀寫使用方法詳解

    CFile與CStdioFile的文件讀寫使用方法詳解

    以下是對(duì)CFile與CStdioFile的文件讀寫使用方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以過來參考下
    2013-09-09
  • C語言Easyx實(shí)現(xiàn)貪吃蛇詳解

    C語言Easyx實(shí)現(xiàn)貪吃蛇詳解

    這篇文章主要為大家詳細(xì)介紹了基于easyx的C++實(shí)現(xiàn)貪吃蛇,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-10-10
  • C語言結(jié)構(gòu)體版學(xué)生成績(jī)管理系統(tǒng)

    C語言結(jié)構(gòu)體版學(xué)生成績(jī)管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了C語言結(jié)構(gòu)體版的學(xué)生成績(jī)管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-02-02
  • 教你Clion調(diào)試ROS包的方法

    教你Clion調(diào)試ROS包的方法

    Clion是一款專門開發(fā)C以及C++所設(shè)計(jì)的跨平臺(tái)的IDE,本文給大家介紹Clion調(diào)試ROS包的方法,感興趣的朋友跟隨小編一起看看吧
    2021-07-07
  • C語言實(shí)現(xiàn)的一個(gè)三子棋游戲詳解流程

    C語言實(shí)現(xiàn)的一個(gè)三子棋游戲詳解流程

    三子棋是一種民間傳統(tǒng)游戲,又叫九宮棋、圈圈叉叉、一條龍、井字棋等。將正方形對(duì)角線連起來,相對(duì)兩邊依次擺上三個(gè)雙方棋子,只要將自己的三個(gè)棋子走成一條線,對(duì)方就算輸了
    2021-10-10
  • ShellExecute函數(shù)用法的實(shí)例代碼

    ShellExecute函數(shù)用法的實(shí)例代碼

    ShellExecute函數(shù)用法的實(shí)例代碼,需要的朋友可以參考一下
    2013-03-03
  • C++嵌套類與局部類詳細(xì)解析

    C++嵌套類與局部類詳細(xì)解析

    從作用域的角度看,嵌套類被隱藏在外圍類之中,該類名只能在外圍類中使用。如果在外圍類之外的作用域使用該類名時(shí),需要加名字限定
    2013-09-09
  • 八皇后問題實(shí)現(xiàn)代碼分享

    八皇后問題實(shí)現(xiàn)代碼分享

    八皇后問題,是一個(gè)古老而著名的問題,是回溯算法的典型案例,這篇文章主要介紹了八皇后問題實(shí)現(xiàn)代碼,需要的朋友可以參考下
    2014-02-02
  • C語言入門篇--局部全局變量的作用域及生命周期

    C語言入門篇--局部全局變量的作用域及生命周期

    本篇文章是c語言基礎(chǔ)篇,本文對(duì)初識(shí)c語言的變量、局部全局變量的作用域及生命周期做了簡(jiǎn)要的概述,希望可以幫助大家快速入門c語言的世界,更好的理解c語言
    2021-08-08
  • C++連接mysql數(shù)據(jù)庫的兩種方法小結(jié)

    C++連接mysql數(shù)據(jù)庫的兩種方法小結(jié)

    這篇文章主要介紹了C++連接mysql數(shù)據(jù)庫的兩種方法小結(jié),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-04-04

最新評(píng)論