An implementation of the skip list data structure written in C++

/* This article describes an implementation of the skip list data structure written in C++.

Traditionally balanced trees have been used to efficiently implement Set and HashMap style data structures. Balanced tree algorithms are characterized by the need to continually rebalance the tree as operations are performed. This is necessary in order to assure good performance.

Skip Lists are a data structure that was developed by William Pugh as a probabilistic alternative to balanced trees. The balancing of a Skip List is achieved through the use of a random number generator.

The benefit of the Skip List is that the algorithms for search, insertion and deletion are rather simple. This provides improved constant time overhead as compared to balanced trees. The algorithms involved are also relatively easy to understand.

In a singly linked list each node contains one pointer to the next node in the list. A node in a Skip List contains an array of pointers. The size of this array is chosen at random (between 0 and some MAX_LEVEL) at the time when the node is created. The size of this array determines the level of the node. For example, a level 3 node has 4 forward pointers, indexed from 0 to 3. The pointer at index 0 (called the level 0 forward pointer) always points to the immediate next node in the list. The other pointers however point to nodes further down the list, and as such if followed allow portions of the list to be skipped over. A level i pointer points to the next node in the list that has level >= i.

What follows is an implementation of a (sorted) Set data structure based on Skip Lists, written in C++.

Retrieved from: http://en.literateprograms.org/Skip_list_(C_Plus_Plus)?oldid=18741
*/

#include <iostream>
#include <cstdlib>
#include <ctime>
#include <cmath>
#include <cstring>  // get the declaration of memset function
using namespace std;

const float P = 0.5;

/*
Each element in the list is stored in a node. The level of the node is chosen randomly when the node is created.
Each SkipNode stores an array of forward pointers as well as a value which represents the element stored in the node. 
The type of this element is templated. We will assume that this type has a default constructor and a < operator.

The maximum level index is 6. C arrays are indexed starting at 0, therefore each node will have between one and seven forward pointers.

*/

const int MAX_LEVEL = 6;

// Structure Definition
template <class T>
struct SkipNode {
    T value;
    SkipNode<T> **forward; // array of pointers

  
  //To create a SkipNode we must first allocate memory for the node itself then allocate memory for the array of forward pointers.
  //The implementation of skip lists given by William Pugh states that the list should be terminated by a special NIL node that 
  //stores a value greater than any legal value. This stipulation was made so that the algorithms described in his paper would 
  //be very simple as they never had to explicitly check for pointers pointing at NIL. I will instead set the initial value of 
  //all pointer fields to the special C value NULL which is defined as 0.A level 0 node will have one forward pointer, a level
  //1 node will have two forward pointers and so on. Therefore we need to add one to the level when allocating memory for the
  //array of forward pointers.

  //SkipNode constructor  

    SkipNode(int level, const T &value)  
    {
        forward = new SkipNode<T> * [level + 1];
        memset(forward, 0, sizeof(SkipNode<T>*)*(level + 1));
        this->value = value;
    }

  // SkipNode destructor

    ~SkipNode() 
    {
        delete [] forward;
    }
};

//A structure that represents a SkipSet is defined. It stores a pointer to a header node. 
//The value stored in the header node is irrelevant and is never accessed. The current 
//level of the set is also stored as this is needed by the insert, delete and search algorithms.


template <class T>
struct SkipSet {
    SkipNode<T> *header;
    int level;


  //SkipSet constructor
  //To create a SkipSet we must first allocate memory for the structure and then allocate memory 
  //for the header node. The initial level of the set is 0 because C arrays are indexed starting at 0.

    SkipSet() {
        header = new SkipNode<T>(MAX_LEVEL, T());
        level = 0;
    }


  //SkipSet Destructor
    ~SkipSet() {
        delete header;
    }


  //print the contents of a skip set
    void print() const;

  //return true if the given value is stored in the set, otherwise false.

    bool contains(const T &) const;

// insert an element into the list

    void insert(const T &);

// delete  an element from the list

    void erase(const T &);
};

//Random Levels:
//Before getting to the main algorithms it is a good idea to tackle the problem of generating random levels for nodes.
// Each node that is created will have a random level between 0 and MAX_LEVEL inclusive. First we create a function 
//that returns a float value between 0.0 and 1.0. The rand() function returns a pseudo-random integer between 0 and RAND_MAX.
// In order to get a float between 0.0 and 1.0 we divide this value by RAND_MAX.


float frand() {
    return (float) rand() / RAND_MAX;
}

//The random number generator should be seeded with the current system time 
//so that the same sequence of random numbers isn't generated every time the program is run.
//The desired function will return a random level between 0 and MAX_LEVEL. A probability
//distribution where half of the nodes that have level i pointers also have level i+1 pointers is used. 
//This gives us a 50% chance of the random_level() function returning 0, a 25% chance of returning 1, a 12.5% chance of
//returning 2 and so on.This function will seed the random number generator the first time it is called. This is achieved
//through the use of a static variable that keeps its value between function calls. This allows the function to seed the 
//generator the first time it is called, and to remember not to seed it again on subsequent calls.


int random_level() {
    static bool first = true;
    if (first) {
        srand( (unsigned)time( NULL ) );
        first = false;
    }

    int lvl = (int)(log(frand())/log(1.-P));
    return lvl < MAX_LEVEL ? lvl : MAX_LEVEL;


//Thisfunction that will print the contents of a skip set to the console. 
//It simply traverses the level 0 pointers and visits every node while printing out the values.

template <class T>
void SkipSet<T>::print() const {
    const SkipNode<T> *x = header->forward[0];
    cout << "{";
    while (x != NULL) {
        cout << x->value;
        x = x->forward[0];
        if (x != NULL)
            cout << ",";
    }    
    cout << "}\n";
}


/*
The search algorithm will return true if the given value is stored in the set, otherwise false.
The pointer x is set to the header node of the list. The search begins at the topmost pointer
of the header node according to the current level of the list. The forward pointers at this 
level are traversed until either a NULL pointer is encountered or a value greater than the 
value being searched for is encountered. When this occurs we attempt to continue the search 
at the next lower level until we have traversed as far as possible. At this point x will point 
at the node with the greatest value that is less than the value being searched for. In the case 
that the search value is less than any value in the list, or if the list is empty, then x will 
still be pointing at the header node.
Finally the level 0 pointer is traversed once. So now there are three possibilities for x.
1. x is pointing at the node with the value we are searching for.
2. x is pointing at a node with value greater than the value we are searching for.
3. x is NULL.
In the first case the search was successful. */


template <class T>
bool SkipSet<T>::contains(const T &search_value) const {
    const SkipNode<T> *x = header;
    for (int i = level; i >= 0; i--) {
        while (x->forward[i] != NULL && x->forward[i]->value < search_value) {
            x = x->forward[i];
        }
    }
    x = x->forward[0];
    return x != NULL && x->value == search_value;
}


/*
To insert a value we first perform the same kind of search as in the search algorithm. 
But, in order to insert a new node into the list we must maintain an array of pointers 
to the nodes that must be updated. */


template <class T>
void SkipSet<T>::insert(const T &value) {
    SkipNode<T> *x = header;
    SkipNode<T> *update[MAX_LEVEL + 1];

    //All of the pointers in the update array must be initialized to NULL.
    memset(update, 0, sizeof(SkipNode<T>*)*(MAX_LEVEL + 1)); //need string.h file

    //Find and record updates
    for (int i = level; i >= 0; i--) {
        while (x->forward[i] != NULL && x->forward[i]->value < value) {
            x = x->forward[i];
        }
        update[i] = x; 
    }
    x = x->forward[0];

    if (x == NULL || x->value != value) {        
        int lvl = random_level();


// Record header updates
//If the new node is greater than any node already in the list 
//then the header node must be updated and the level of the list 
//must be set to the new level.


        if (lvl > level) {
   for (int i = level + 1; i <= lvl; i++) {
       update[i] = header;
   }
   level = lvl;
}

//Insert new node
//Two things must be done to insert the node. We must make the new 
//node x point at what the nodes in the update vector are currently 
//pointing at. Then we update the nodes in the |update| vector to point at x.


        x = new SkipNode<T>(lvl, value);
for (int i = 0; i <= lvl; i++) {
   x->forward[i] = update[i]->forward[i];
   update[i]->forward[i] = x;
}
    }
}


/*
The deletion algorithm starts the same way as the insertion algorithm. 
We declare an update array and then search for the node to be deleted. 
If the node is found we set the nodes in the update array to point at 
what |x| is pointing at. This effectively removes x from the list and 
so the memory occupied by the node may be freed. */


template <class T>
void SkipSet<T>::erase(const T &value) {
    SkipNode<T> *x = header;
    SkipNode<T> *update[MAX_LEVEL + 1];

    //Set update pointers to NULL
    memset(update, 0, sizeof(SkipNode<T>*)*(MAX_LEVEL + 1));

    // Find and record updates
    for (int i = level; i >= 0; i--) {
        while (x->forward[i] != NULL && x->forward[i]->value < value) {
            x = x->forward[i];
        }
        update[i] = x; 
    }
    x = x->forward[0];

    if (x->value == value) {
      // Remove x from list
        for (int i = 0; i <= level; i++) {
   if (update[i]->forward[i] != x)
       break;
   update[i]->forward[i] = x->forward[i];
}
        delete x;

//After deleting the node we must check to see if the level 
//of the list must be lowered.
        while (level > 0 && header->forward[level] == NULL) {
   level--;
}
    }
}


//For simple illustration purposes we create a SkipSet and perform some tests.
int main() {

    SkipSet<int> ss;

    ss.print();

    ss.insert(5);
    ss.insert(10);
    ss.insert(7);
    ss.insert(7);
    ss.insert(6);
    
    if (ss.contains(7)) {
        cout << "7 is in the list\n";
    }

    ss.print();

    ss.erase(7);
    ss.print();
    
    if (!ss.contains(7)) {
        cout << "7 has been deleted\n";
    }

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目标检测(Object Detection)是计算机视觉领域的一个核心问题,其主要任务是找出图像中所有感兴趣的目标(物体),并确定它们的类别和位置。以下是对目标检测的详细阐述: 一、基本概念 目标检测的任务是解决“在哪里?是什么?”的问题,即定位出图像中目标的位置并识别出目标的类别。由于各类物体具有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具挑战性的任务之一。 二、核心问题 目标检测涉及以下几个核心问题: 分类问题:判断图像中的目标属于哪个类别。 定位问题:确定目标在图像中的具体位置。 大小问题:目标可能具有不同的大小。 形状问题:目标可能具有不同的形状。 三、算法分类 基于深度学习的目标检测算法主要分为两大类: Two-stage算法:先进行区域生成(Region Proposal),生成有可能包含待检物体的预选框(Region Proposal),再通过卷积神经网络进行样本分类。常见的Two-stage算法包括R-CNN、Fast R-CNN、Faster R-CNN等。 One-stage算法:不用生成区域提议,直接在网络中提取特征来预测物体分类和位置。常见的One-stage算法包括YOLO系列(YOLOv1、YOLOv2、YOLOv3、YOLOv4、YOLOv5等)、SSD和RetinaNet等。 四、算法原理 以YOLO系列为例,YOLO将目标检测视为回归问题,将输入图像一次性划分为多个区域,直接在输出层预测边界框和类别概率。YOLO采用卷积网络来提取特征,使用全连接层来得到预测值。其网络结构通常包含多个卷积层和全连接层,通过卷积层提取图像特征,通过全连接层输出预测结果。 五、应用领域 目标检测技术已经广泛应用于各个领域,为人们的生活带来了极大的便利。以下是一些主要的应用领域: 安全监控:在商场、银行
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值