跳表的实现

// SkiplistNode: 定义跳表的节点结构
struct SkiplistNode
{
    int _val;  // 节点的值
    vector<SkiplistNode*> _nextV;  // 存储每层的下一个节点的指针

    // 构造函数:初始化节点的值和每一层的下一个指针
    SkiplistNode(int val , int level)
            :_val(val)  // 节点的值
            ,_nextV(level , nullptr)  // 每层的下一个节点初始化为nullptr,大小为level
    {

    }
};

SkiplistNode 是跳表中的节点结构体。每个节点包含:

  • 一个整数值 _val,即节点存储的数据。
  • 一个 vector<SkiplistNode*> _nextV,表示当前节点在不同层级上的"右"节点指针。每一层都有一个指针,_nextV[i] 指向当前节点在第 i 层的下一个节点。
  • SkiplistNode(int val, int level) 是构造函数,初始化节点的值为 val,并为每一层的 next 指针分配空间,初始为 nullptr
// Skiplist 类:跳表的实现
class Skiplist {
    typedef SkiplistNode  Node;  // 为 SkiplistNode 起别名 Node,简化代码书写
public:
    Skiplist() 
    { 
        srand(time(0));  // 初始化随机种子,用于生成跳表节点的层数
        _head = new SkiplistNode(-1 ,1);  // 初始化跳表的头节点,层数为1
    }

Skiplist 类是跳表的核心实现。

  • typedef SkiplistNode NodeSkiplistNode 定义了一个别名 Node,让代码更简洁。
  • 构造函数 Skiplist() 中:
    • srand(time(0)) 用于初始化随机数生成器的种子,使得每次运行时跳表的生成更加随机。
    • _head = new SkiplistNode(-1, 1) 创建了跳表的头节点,值为 -1,并且只有 1 层。头节点不存储实际数据,仅用于跳表的操作。

    // search: 查找目标值是否在跳表中
    bool search(int target) 
    {
        Node* cur = _head;  // 从头节点开始遍历
        int level = _head->_nextV.size() - 1;  // 从跳表的最高层开始查找

        // 遍历每一层
        while (level >= 0)
        {
            // 如果目标值比当前层下一个节点的值小,或者下一个节点为空,向下走
            if (cur->_nextV[level] && cur->_nextV[level]->_val < target)
            {
                cur = cur->_nextV[level];  // 向右移动
            }
            else if (cur->_nextV[level] == nullptr || cur->_nextV[level]->_val > target)
            {
                level--;  // 向下走
            }
            else  // 找到目标值
            {
                return true;
            }
        }

        return false;  // 如果遍历完都没有找到,返回 false
    }
  • search 函数用于查找目标值 target 是否在跳表中。
    • Node* cur = _head; 从头节点开始。
    • int level = _head->_nextV.size() - 1; 从跳表的最高层(即 level 层)开始遍历。
    • while (level >= 0) 循环遍历每一层,直到遍历到最低层。
      • if (cur->_nextV[level] && cur->_nextV[level]->_val < target) 判断当前层的下一个节点的值是否小于目标值 target,如果是,说明目标值可能在当前节点的右侧,cur = cur->_nextV[level] 向右走。
      • else if (cur->_nextV[level] == nullptr || cur->_nextV[level]->_val > target) 如果当前节点的下一个节点为空或值大于目标值,向下走,level--
      • else 如果当前节点的下一个节点的值等于目标值,返回 true,表示找到了目标值。

    // FindPreNode: 查找插入节点时,每一层的前一个节点
    vector<Node*> FindPreNode(int num)
    {
        Node* cur = _head;  // 从头节点开始遍历
        int level = _head->_nextV.size() - 1;  // 从最高层开始

        vector<Node*> preV(level + 1, _head);  // 用一个 vector 来存储每层的前一个节点,初始化为头节点

        // 遍历每一层
        while (level >= 0)
        {
            // 如果当前层的下一个节点值小于 num,向右走
            if (cur->_nextV[level] && cur->_nextV[level]->_val < num)
            {
                cur = cur->_nextV[level];  // 向右移动
            }
            else if (cur->_nextV[level] == nullptr || cur->_nextV[level]->_val >= num)
            {
                preV[level] = cur;  // 更新当前层的前一个节点
                level--;  // 向下走
            }
        }
        return preV;  // 返回每一层的前一个节点
    }
  • FindPreNode 函数查找目标值 num 的插入位置,返回每一层的前一个节点。
    • vector<Node*> preV(level + 1, _head) 用一个 vector 存储每一层的前一个节点,初始化为头节点。
    • while (level >= 0) 遍历每一层,直到最低层。
      • 如果当前层的下一个节点值小于目标值 num,就向右移动 cur = cur->_nextV[level]
      • 如果当前层的下一个节点为空或值大于等于目标值,更新当前层的前一个节点,并向下走。

    // add: 添加一个新的节点到跳表
    void add(int num) 
    {
        vector<Node*> preV = FindPreNode(num);  // 查找插入位置
        int n = Randomlevel();  // 随机生成该节点的层数
        Node* newnode = new Node(num, n);  // 创建新节点

        // 如果生成的层数超过了头节点的层数,扩展头节点的层数
        if (n > _head->_nextV.size())
        {
            _head->_nextV.resize(n, nullptr);  // 扩展头节点的层数
            preV.resize(n, _head);  // 更新前一个节点的数组
        }

        // 连接新节点与前后节点
        for (size_t i = 0; i < n; i++)
        {
            newnode->_nextV[i] = preV[i]->_nextV[i];  // 新节点的右节点指向前一个节点的右节点
            preV[i]->_nextV[i] = newnode;  // 前一个节点的右节点指向新节点
        }
    }

add 函数用于将一个新的值 num 插入到跳表中。

  • FindPreNode(num) 查找插入位置,返回每一层的前一个节点。
  • Randomlevel() 随机生成新节点的层数。
  • new Node(num, n) 创建一个新的节点,层数为 n
  • 如果生成的层数超过了头节点的当前层数,扩展头节点的层数。
  • 通过 newnode->_nextV[i]preV[i]->_nextV[i],将新节点和前后节点连接起来。

     // erase: 删除跳表中的某个节点
    bool erase(int num) 
    {
        vector<Node*> preV = FindPreNode(num);  // 查找删除节点的前一个节点
        // 如果第一层的下一个节点不是目标节点,说明目标节点不存在
        if (preV[0]->_nextV[0] == nullptr || preV[0]->_nextV[0]->_val != num)
        {
            return false;  // 返回 false,表示删除失败
        }
        else {
            Node* del = preV[0]->_nextV[0];  // 找到需要删除的节点
            // 遍历每一层,将前一个节点的下一个节点指向当前节点的下一个节点
            for (size_t i = 0; i < del->_nextV.size(); i++)
            {
                preV[i]->_nextV[i] = del->_nextV[i];  // 前一个节点指向当前节点的下一个节点
            }
            delete del;  // 删除节点

            // 如果最高层的节点被删除,调整头节点的层数
            int i = _head->_nextV.size() - 1;
            while (i >= 0)
            {
                if (_head->_nextV[i] == nullptr)
                    --i;
                else
                    break;
            }
            _head->_nextV.resize(i + 1);  // 调整头节点的层数

            return true;  // 返回 true,表示删除成功
        }
    }

erase 函数用于删除跳表中的某个节点。

  • FindPreNode(num) 查找删除节点的前一个节点。
  • 如果节点不存在(即第一层的下一个节点不是目标节点),返回 false
  • 否则,找到目标节点,遍历每一层,调整前一个节点的右指针,跳过删除节点,最后删除该节点。
  • 删除节点后,如果最高层的节点被删除,更新头节点的层数。

    // Randomlevel: 随机生成跳表节点的层数
    int Randomlevel()
    {
        size_t level = 1;  // 默认最小层数为1
        while (rand() <= RAND_MAX * _p && level < _maxlevel)
        {
            level++;  // 持续随机生成更高的层数,直到随机停止
        }
        return level;  // 返回生成的层数
    }

Randomlevel 用于随机生成跳表节点的层数。

  • rand() <= RAND_MAX * _p 是一个概率判断,决定是否继续提升层数,_p 是控制层数概率的参数。
  • level 从 1 开始,每次随机决定是否增加层数,直到达到最大层数 _maxlevel

  • 跳表是一种通过多层索引来加速查找、插入和删除操作的高级数据结构。
  • 本实现通过使用随机数来决定每个节点的层数,利用多层链表的结构来提高操作效率。
  • 关键操作包括查找、插入、删除和层数生成,这些操作都依赖于跳表的层次结构和随机性。

完整代码

struct SkiplistNode
{
    int _val;
    vector<SkiplistNode*> _nextV;

    SkiplistNode(int val , int level)
            :_val(val)
            ,_nextV(level , nullptr)
    {

    }
};

class Skiplist {
    typedef SkiplistNode  Node;
public:
    Skiplist() 
    { 
        srand(time(0));
        _head = new SkiplistNode(-1 ,1);//头节点 层数是1

    }
    
    bool search(int target) 
    {
       Node* cur = _head;
		int level = _head->_nextV.size() - 1;
		while (level >= 0)
		{
			// 目标值比下一个节点值要大,向右走
			// 下一个节点是空(尾),目标值比下一个节点值要小,向下走
			if (cur->_nextV[level] && cur->_nextV[level]->_val < target)
			{
				// 向右走
				cur = cur->_nextV[level];
			}
			else if (cur->_nextV[level] == nullptr || cur->_nextV[level]->_val > target)
			{
				// 向下走
				--level;
			}
			else
			{
				return true;
			}
		}

		return false;

    }
    
    vector<Node*> FindPreNode(int  num)
    {
         Node* cur = _head;
        int level = _head->_nextV.size()-1; //最高层

        vector<Node*> preV(level+1 , _head);//插入位置每一层 前一个节点指针

        while(level>=0)
        {
            //目标值比下一个节点值大 向右走
            //下一个节点为空或者 目标值比下一个节点值小 向下走
            
            if(cur->_nextV[level] && cur->_nextV[level]->_val  <num)
            {
                //向右走
                cur = cur->_nextV[level];
            }
             else if(cur->_nextV[level] == nullptr|| cur->_nextV[level]->_val >= num)
            {
                //更新level层前一个
                preV[level]  =cur;
                //向下走
                level--;
            }
        }
        return preV;
    }


    void add(int num) 
    {
        vector<Node*> preV = FindPreNode(num);

        int n = Randomlevel();
        Node * newnode = new Node(num , n);
        //head层数可能不够
        if(n> _head->_nextV.size())
        {
             _head->_nextV.resize(n,nullptr);
             preV.resize(n,_head);
        }
            
        //链接前后节点
        for(size_t i = 0 ;i<n ;i++)
        {
            newnode->_nextV[i] = preV[i]->_nextV[i];
            preV[i]->_nextV[i] = newnode;
        }

    }
    
    bool erase(int num) 
    {
        vector<Node*> preV = FindPreNode(num);
        //第一层下一个不是val  val不在
        if(preV[0]->_nextV[0] == nullptr ||preV[0]->_nextV[0]->_val !=num )
        {
            return false;
        }
        else{
            Node* del = preV[0]->_nextV[0];
            //每一层依次更新 前后节点相连
            for(size_t i = 0 ; i<del->_nextV.size();i++)
            {
                preV[i]->_nextV[i]  =del->_nextV[i];
            }
            delete del;

            // 如果删除最高层节点,把头节点的层数也降一下
			int i = _head->_nextV.size()- 1;
			while (i >= 0)
			{
				if (_head->_nextV[i] == nullptr)
					--i;
				else
					break;
			}
			_head->_nextV.resize(i + 1);

             return true;
        }
       

    }

    int Randomlevel()
    {
        size_t level = 1;
        while(rand() <=RAND_MAX*_p && level <_maxlevel)
        {
            level++;
        }
        return level;
    }

    /*void Print()
    {
        int level = _head->_nextV.size();
        for(int i = level-1; i>=0;i--)
        {
            Node *cur =_head;
            while(cur)
            {
                printf("%d->",cur->_val);
                cur = cur->_nextV[i];
            }
            printf("\n");
        }
    }*/
private:  
    Node *  _head; //给一个头节点
    size_t _maxlevel = 32;
    double _p = 0.25;
};


### C++ 实现跳表数据结构 跳表是一种可以在 \(O(\log n)\) 时间复杂度内完成插入、删除和查找操作的数据结构[^3]。相比其他平衡树结构如红黑树,跳表具有较短的代码量以及易于理解和实现的特点。 下面展示一段用于创建并初始化跳表节点的C++代码: ```cpp const int MAX_LEVEL = 16; struct Node { int key; double score; // 值域可以自定义调整 std::vector<Node*> forward; // 存储指针数组 explicit Node(int k, double s):key(k),score(s){ forward.resize(MAX_LEVEL); } }; ``` 为了管理整个跳跃列表,还需要构建一个类来封装这些基本功能: ```cpp class SkipList { private: static const double P = 0.5; /* 随机级别提升概率 */ int level; /* 当前层数 */ int size_; /* 列表中的元素数量 */ Node* header; /* 头结点 */ public: SkipList() : level(1), size_(0) { header = new Node(-1,-1); // 初始化头结点 srand((unsigned)time(NULL)); } ~SkipList(){ clear(); delete header; } void insert(int key,double score){...} bool remove(int key){...} Node* search(int key)const{...} ... }; ``` 上述代码片段展示了如何声明 `Node` 结构体及其成员变量,并通过 `std::vector` 来动态分配每一层所需的链接空间[^2]。此外还给出了 `SkipList` 类的部分接口函数原型说明,包括增删查等功能方法。 对于完整的插入、删除及查询逻辑,则涉及到更多细节上的处理,比如随机化层次的选择机制等,在此不再赘述具体算法流程。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

青山是哪个青山

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值