C++ SkipList简单实现

本文详细介绍了跳表的数据结构,包括其核心概念、工作原理以及如何通过C++进行实现。跳表是一种高效的数据结构,用于快速查找、插入和删除操作,它通过引入多级索引来平衡搜索效率和空间复杂度。文章提供了完整的跳表类定义,包括查找、插入和删除等关键方法,并通过一个示例展示了如何使用跳表处理大量数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SkipList.h:

#pragma once
#include <vector>

struct SkipListNode
{
	SkipListNode(int data, int level);

	int m_data;
	std::vector<SkipListNode*> m_forward;
};

class SkipList
{
public:
	SkipList();
	~SkipList();

public:
	bool Find(int data);
	bool Insert(int data);
	bool Remove(int data);

public:
	void Print();

private:
	void FirstInsert(int data);
	bool IsEmpty();

private:
	SkipListNode* m_pHead;
	int m_count;
	int m_level;
};

SkipList.cpp

#include "SkipList.h"

#include <random>
#include <iostream>

int GetRandLevel()
{
	int destLevel = 1;
	while (1 == rand() % 2) ++destLevel;
	return destLevel;
}

SkipListNode::SkipListNode(int data, int level)
	: m_data(data)
	, m_forward(level)
{

}

SkipList::SkipList()
	: m_pHead(nullptr)
	, m_count(0)
	, m_level(1)
{
	auto pHead = new SkipListNode(INT_MIN, m_level);
	m_pHead = pHead;
}

SkipList::~SkipList()
{
	auto* pNode = m_pHead;
	while (pNode != nullptr)
	{
		auto pNextNode = pNode->m_forward[0];
		delete pNode;
		pNode = pNextNode;
	}
}

bool SkipList::Find(int data)
{
	if (IsEmpty()) return false;

	auto pCurNode = m_pHead;
	for (int curLevel = m_level - 1; curLevel >= 0; --curLevel)
	{
		// 判断是否已经走到该层最后一个元素
		if (nullptr == pCurNode->m_forward[curLevel]) continue;
		while (pCurNode->m_forward[curLevel]->m_data < data)
		{
			pCurNode = pCurNode->m_forward[curLevel];

			// 判断是否已经走到该层最后一个元素
			if (nullptr == pCurNode->m_forward[curLevel]) break;
		}
	}

	if (data == pCurNode->m_forward[0]->m_data)
	{
		return true;
	}

	return false;
}

void SkipList::Print()
{
	auto* pCurNode = m_pHead;
	while (pCurNode != nullptr)
	{
		std::cout << pCurNode->m_data << "(" << int(pCurNode->m_forward.size()) << ")" << " ";
		pCurNode = pCurNode->m_forward[0];
	}

	std::cout << std::endl;
}

void SkipList::FirstInsert(int data)
{
	int destLevel = GetRandLevel();
	if (destLevel > m_level)
	{
		destLevel = ++m_level; // 更新最高层数, 只增加一层
		m_pHead->m_forward.resize(m_level);
	}

	auto pNewNode = new SkipListNode(data, destLevel);

	for (int curLevel = m_level - 1; curLevel >= 0; --curLevel)
	{
		m_pHead->m_forward[curLevel] = pNewNode;
		pNewNode->m_forward[curLevel] = nullptr;
	}

	++m_count;
	return;
}

bool SkipList::IsEmpty()
{
	return 0 == m_count;
}

bool SkipList::Insert(int data)
{
	if (0 == m_count) // 首次插入
	{
		FirstInsert(data);
		return true;
	}

	auto pNode = m_pHead;

	// 需要更新下一跳指针的节点
	std::vector<SkipListNode*> updates(m_level); 
	for (int curLevel = m_level - 1; curLevel >= 0; --curLevel)
	{
		if (nullptr == pNode->m_forward[curLevel])
		{
			updates[curLevel] = pNode;
			continue;
		}

		while (pNode->m_forward[curLevel]->m_data < data)
		{
			pNode = pNode->m_forward[curLevel];
			if (nullptr == pNode->m_forward[curLevel])
				break;
		}

		updates[curLevel] = pNode;
	}

	if ((pNode->m_forward[0] != nullptr) && (data == pNode->m_forward[0]->m_data))
	{
		// 已有节点处理
		return false;
	}

	int destLevel = GetRandLevel();
	if (destLevel > m_level)
	{
		destLevel = ++m_level; // 更新最高层数
		updates.resize(m_level);
		updates[m_level - 1] = m_pHead;	
		m_pHead->m_forward.resize(m_level);
	}

	// 待插入元素
	auto* pNewNode = new SkipListNode(data, destLevel);

	// 从插入节点的最高层开始向下处理,对插入节点最高层之上的指针无影响
	for (int curLevel = destLevel - 1; curLevel >= 0; --curLevel)
	{
		pNewNode->m_forward[curLevel] = updates[curLevel]->m_forward[curLevel];
		updates[curLevel]->m_forward[curLevel] = pNewNode;
	}

	// 增加元素个数
	++m_count; 
	return true;
}

bool SkipList::Remove(int data)
{
	auto* pCurNode = m_pHead;
	if (IsEmpty()) return false;

	// 需要更新下一跳指针的节点
	std::vector<SkipListNode*> updates(m_level);
	for (int curLevel = m_level - 1; curLevel >= 0; --curLevel)
	{
		if (nullptr == pCurNode->m_forward[curLevel])
		{
			updates[curLevel] = pCurNode;
			continue;
		}

		while (pCurNode->m_forward[curLevel]->m_data < data)
		{
			pCurNode = pCurNode->m_forward[curLevel];
			if (nullptr == pCurNode->m_forward[curLevel])
				break;
		}

		updates[curLevel] = pCurNode;
	}

	if (data != pCurNode->m_forward[0]->m_data)
	{
		// 没有该节点
		return false;
	}

	// 待删除元素
	auto* pRmoveNode = pCurNode->m_forward[0];

	// 将原来指向待删除元素的指针指向待删除元素的下一个元素
	for (int curLevel = m_level - 1; curLevel >= 0; --curLevel)
	{
		if (updates[curLevel]->m_forward[curLevel] == pRmoveNode)
			updates[curLevel]->m_forward[curLevel] = pRmoveNode->m_forward[curLevel];
	}

	// 修改层高,将为空的层删除
	for (int curLevel = m_level - 1; curLevel >= 0; --curLevel)
	{
		if (nullptr == m_pHead->m_forward[curLevel])
			--m_level;
	}

	m_pHead->m_forward.resize(m_level + 1);

	// 元素个数-1
	--m_count;
}

main.cpp:

#include <vector>
#include <iostream>
#include <algorithm>

int main()
{
	SkipList skipList;

	std::vector<int> datas;
	datas.reserve(100);
	for (int idx = 0; idx < 100; ++idx)datas.push_back(idx);
	std::random_shuffle(datas.begin(), datas.end());

	for (auto& data : datas) skipList.Insert(data);
	skipList.Print();

	std::cout << "--------------------------------" << std::endl;

	for (auto& data : datas) skipList.Remove(data);
	skipList.Print();
	return 0;
}

C++是一种面向对象的计算机程序设计语言,由美国AT&T贝尔实验室的本贾尼·斯特劳斯特卢普博士在20世纪80年代初期发明并实现(最初这种语言被称作“C with Classes”带类的C)。它是一种静态数据类型检查的、支持多重编程范式的通用程序设计语言。它支持过程化程序设计、数据抽象、面向对象程序设计、泛型程序设计等多种程序设计风格。C++是C语言的继承,进一步扩充和完善了C语言,成为一种面向对象的程序设计语言。C++这个词在中国大陆的程序员圈子中通常被读做“C加加”,而西方的程序员通常读做“C plus plus”,“CPP”。 在C基础上,一九八三年又由贝尔实验室的Bjarne Strou-strup推出了C++C++进一步扩充和完善了C语言,成为一种面向 对象的程序设计语言。C++目前流行的编译器最新版本是Borland C++ 4.5,Symantec C++ 6.1,和Microsoft Visual C++ 2012。C++提出了一些更为深入的概念,它所支持的这些面向对象的概念容易将问题空间直接地映射到程序空间,为程序员提供了一种与传统结构程序设计不同的思维方式和编程方法。因而也增加了整个语言的复杂性,掌握起来有一定难度。C++由美国AT&T贝尔实验室的本贾尼·斯特劳斯特卢普博士在20世纪80年代初期发明并实现(最初这种语言被称作“C with Classes”带类的C)。开始,C++是作为C语言的增强版出现的,从给C语言增加类开始,不断的增加新特性。虚函数(virtual function)、运算符重载(Operator Overloading)、多重继承(Multiple Inheritance)、模板(Template)、异常(Exception)、RTTI、命名空间(Name Space)逐渐被加入标准。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值