公司网站需要备案,个人网站可以做音乐吗,北京外贸行业网站建设,河南免费网站建设公司推荐目录标题 为什么会有跳表跳表的原理跳表的模拟实现准备工作find函数insert函数erase函数 测试效率比较 为什么会有跳表
在前面的学习过程中我们学习过链表这个容器#xff0c;这个容器在头部和尾部插入数据的时间复杂度为O(1)#xff0c;但是该容器存在一个缺陷就是不管数据… 目录标题 为什么会有跳表跳表的原理跳表的模拟实现准备工作find函数insert函数erase函数 测试效率比较 为什么会有跳表
在前面的学习过程中我们学习过链表这个容器这个容器在头部和尾部插入数据的时间复杂度为O(1)但是该容器存在一个缺陷就是不管数据是否有序查找数据是否存在的时间复杂度都是O(N),我们只能通过暴力循环的方式查找数据是否存在尽管数据是有序的我们也不能通过二分查找的形式去查找一个数据那么为了解决这个问题就有人提出来了跳表这个容器。
跳表的原理
之前学习的链表结构如下 那么跳表的思路就是每相邻两个节点升高一层增加一个指针让指针指向下下个节点 这样所有新增加的指针连成了一个新的链表但它包含的节点个数只有原来的一半。由于新增加的指针我们不再需要与链表中每个节点逐个进行比较了需要比较的节点数大概只有原来的一半。比如说要查找元素19首先就比较头结点最上面指针指向的节点节点中的元素为6是否大于19如果大于就跳转到该节点那么很明显这里是大于的所以跳转到节点6上然后接着比较元素6最上面的指针指向的节点节点9是否大于19很明显是晓得所以跳转到元素9上然后接着就来到节点17因为节点17最上面的节点指向的元素是21比19大那么这时就不能往该指针指向的节点进行跳转我们得比较该指针下面的指针17号节点中下方指针指向的节点该节点指向的值刚好为19所以就找到了指定元素那么这就是跳表查找元素的原理
可是上面的查找过程并没有提高很大的效率最好的情况也是O(N/2)所以为了进一步的提高效率我们在第二层新产生的链表上继续为每相邻的两个节点升高一层增加一个指针从而产生第三层链表 那么这时查找元素19就会变的更快经过一次比较就来到了节点9然后就跳转到节点17最后就来到了节点19 可以看到再添加一层节点就会使得节点查找的效率变的更高那么同样的道理只要链表的数据个数够长我们还可以添加多层这样以此类推直到无法添加节点为止那么这个时候进行第一次比较就可以过滤掉一半的元素再经历一次比较又可以过滤掉1/4的元素再经历一次比较就可以过滤掉1/8的元素等等这样一直循环下去我们就可以发现此时查找元素的时间复杂度为O(logN),但是这里存在一个问题虽然这样的结构能够带来logN的效率但是该效率是用整齐的结构换过来的如果在使用的过程中对元素进行插入和删除这种结构就会被破坏比如说最高的层数为10如果我要往中间的节点插入一个节点的话这个节点的层数是多少呢1到10都可以对吧但是插入之后链表就没办法保持之前的规律(每相邻的节点抬高一层)如果要维持这种对应关系就必须把新插入的节点后面的所有节点也包括新插入的节点重新进行调整这会让时间复杂度重新蜕化成O(n)。skiplist的设计为了避免这种问题做了一个大胆的处理不再严格要求对应比例关系而是插入一个节点的时候随机出一个层数。这样每次插入和删除都不需要考虑其他节点的层数这样就好处理多了。 那这里就存在一个问题如果时随机分配层数的话那如何来保证效率呢随机数有很多很多1是随机数1w也是随机数并且还可能出现多个很大的随机数比如说当前有100个节点但是有99个节点的高度为100但是我们需要很多高层的节点吗好像不需要对吧理论上来说层数越高的节点出现的概率应该越小所以为了提高跳表的空间效率和时间效率我们给跳表设计一个最大层数maxLevel的限制其次再设置一个多增加一层的概率P也就是说每个节点的高度至少为1每升高一层的概率p那么一个节点的高度为1的概率是1-P本来就有一个层高度不增加的概率为1-p所以高度为1的概率是1-P同样的道理高度为1增高一层的概率为p不增加的概率为1-p所以层数为二的概率就是p*(1-p)节点层数为3的概率就是p*p*(1-p)这样以此类推就不难发现节点层数越高出现的概率越小 那么这就是跳表的原理接下来我们来看看如何实现跳表。
跳表的模拟实现
准备工作
首先每个节点的中存在一个变量用来存储数据然后还需要一堆指针来记录下一个节点的位置指针的数量不清楚而且又不止一个所以我们可以创建一个vector对象来存储指针那么这里我们就可以创建一个类来描述指针该类的构造函数就需要两个参数一个参数表示节点的层数另外一个参数表示节点存储的数据然后在构造函数里面就对数组进行初始化将每个指针都初始化为空那么这里的代码如下
templateclass T
struct ListNode
{typedef ListNodeT Node;ListNode(T val,int size):_nextV(size,nullptr),_val(val){}T _val;vectorNode* _nextV;
};然后在跳表类里面就存储一个指针变量用来记录头结点然后创建另个变量用来记录当前节点的最大层数和增加层数的概率在构造函数里面将这个指针指向一个新创建的节点节点的高度为1存储的值为元素的默认构造并且后面的代码我们要用到随机数这个东西所以在构造函数里面还得添加一个时间戳那么这里的代码如下
templateclass T
class Skiplist
{
public:typedef ListNodeT Node;Skiplist(){srand(time(0));_head new Node(T(), 1);}private:Node* _head;int _maxLevel 32;double _p 0.5;
};find函数
find函数是这个类的关键因为这里我们不仅要找到这个元素还得找到直线这个元素的其他节点比如我们要查找元素21: 在节点21前面存在很多节点指向21比如说9号节点17号节点19号节点这三个节点都指向节点21那么find函数不仅要判断21是否存在还得返回指向21号节点的节点之所以这么做是为了方便后面的insert函数和erase函数但是这里存在一个问题我们知道了哪个节点指向21但是不知道是这些节点中的哪个指针指向21所以这个时候就得将数组中的下表结合起来因为一个高度的指针只有一个指针指向某个节点所以我们就可以用数组的下表来进行辅助判断如果数组中下表为1的元素中记录的节点是9号节点的话就表明9号节点的中下表为1的指针指向了该节点那么这样find函数不仅可以查询节点是否存在还可以找到哪些位置指向该节点这样可以方便后面的插入函数和删除函数那么首先这个函数需要一个T类型的参数然后返回值是vector类型并且vector中的元素类型是Node*
vectorNode* find(const T target)
{}然后在函数里面就可以创建一个vector将其大小初始化为头结点的长度然后创建一个变量level用来存储头结点中数组的个数因为我们要不停比较节点中的值所以还得创建一个Node类型的指针用来指向当前寻找的节点然后就可以创建一个while循环因为循环的目的是将每一个指向目标节点的节点都记录下来所以循环结束的条件就是level大于等于0那么这里代码如下
vectorNode* find(const T target)
{int level _head-_nextV.size()-1;//这里是下表所以要减一vectorNode* tmp(level1,_head);//这里要加一并且每个元素都指向头结点Node* cur _head;while (level 0){}
}循环里面我们就要做出判断当前cur指向的节点的level层指针指向的元素是否小于target如果小于就将cur指向level层指针指向的元素如果大于target或者当前的指针指向的元素为空就说明找到了指向插入位置或者要删除元素的前一个节点那么这个时候就将该节点的地址填入数组中的level下表然后将level的值减减即可那么这里额度代码如下
vectorNode* find(const T target)
{int level _head-_nextV.size()-1;//这里是下表所以要减一vectorNode* tmp(level1,_head);//这里要加一并且每个元素都指向头结点Node* cur _head;while (level 0){// 目标值比下一个节点值要大向右走// 下一个节点是空(尾)目标值比下一个节点值要小向下走if (cur-_nextV[level] ! nullptr cur-_nextV[level]-_val target){// 向右走cur cur-_nextV[level];}else if (cur-_nextV[level] nullptr || cur-_nextV[level]-_val target){// 更新level层前一个tmp[level] cur;// 向下走level--;}}return tmp;
}当然这样的find函数还是太难用了所以我们还是添加一个简化版的find函数那么这里的思路是一样的我们就直接看代码
bool search(T 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;
}insert函数
当出现重复值时插入可能失败所以insert函数的返回值为bool函数的参数为T类型
bool insert(T num)
{
}那么在函数的开始就得创建一个数组用来接收find函数的返回值然后我们得生成一个随机数用来记录数的高度那么这里我们还得写一个函数用来随机创建树的高度那么这里的代码如下
int RandomLevel()
{size_t level 1;// rand() -[0, RAND_MAX]之间while (rand() RAND_MAX*_p level _maxLevel){level;}return level;
}我们知道rand函数生成的数据大小是有范围的那么我们就可以利用这个范围来生成随机高度有了这个函数之后我们就可以得到高度然后new出来节点并将节点的高度设为函数的返回值那么这里的代码如下
bool insert(T num)
{vectorNode* prevV FindPrevNode(num);int height RandomLevel();Node* newnode new Node(num, height);
}因为插入一个节点之后可能该节点的高度会刷新整个链表的高度所以我们得进行判断如果新创建出来的节点高度大于头结点的高度就得对头节点和上面的prevV进行扩长那么这里就得添加一个if语句
bool insert(T num)
{vectorNode* prevV FindPrevNode(num);int height RandomLevel();Node* newnode new Node(num, height);if (height _head-_nextV.size()){_head-_nextV.resize(height, nullptr);prevV.resize(height, _head);}
}然后就要将prevV数组中的节点高度个元素的指针指向进行改变让新插入的节点指向他们之前指向的节点然后让这些节点指向新插入的节点比如说下面的图片要插入元素15 插入之后就变成这样 而数组prevV中刚好记录所有指向该位置的节点那么这里就可以创建一个while循环进行更改那么完整的代码如下
bool insert(T num)
{vectorNode* prevV find(num);int height RandomLevel();Node* newnode new Node(num, height);if (height _head-_nextV.size()){_head-_nextV.resize(height, nullptr);prevV.resize(height, _head);}for (int i 0; i height; i){newnode-_nextV[i] prevV[i]-_nextV[i];prevV[i]-_nextV[i] newnode;}
}erase函数
erase函数的实现思路也是一样的首先得判断一下当前要删除的元素是否存在如果不存在的话就直接返回false如果存在的话先创建一个数组和find函数一起记录指向该节点的节点然后跟insert函数一样修改节点中指针的指向将指向该节点的指针指向该节点的下一个那么这里的代码如下
bool erase(T num)
{vectorNode* prevV FindPrevNode(num);// 第一层下一个不是valval不在表中if (prevV[0]-_nextV[0] nullptr || prevV[0]-_nextV[0]-_val ! num){return false;}else{Node* del prevV[0]-_nextV[0];// del节点每一层的前后指针链接起来for (size_t i 0; i del-_nextV.size(); i){prevV[i]-_nextV[i] del-_nextV[i];}delete del;}
}但是这里存在一个问题如果将最高节点删除了那头节点是不是也得进行变化呢所以这里还得查看一下头结点中不为空指针的数量然后将其长度进行缩小那么完整的代码如下
bool erase(T num)
{vectorNode* prevV FindPrevNode(num);// 第一层下一个不是valval不在表中if (prevV[0]-_nextV[0] nullptr || prevV[0]-_nextV[0]-_val ! num){return false;}else{Node* del prevV[0]-_nextV[0];// del节点每一层的前后指针链接起来for (size_t i 0; i del-_nextV.size(); i){prevV[i]-_nextV[i] del-_nextV[i];}delete del; }int i _head-_nextV.size() - 1;while (i 0){if (_head-_nextV[i] nullptr)--i;elsebreak;}_head-_nextV.resize(i 1);return true;
}测试
为了方便测试我们可以添加一个打印函数
void Print()
{Node* cur _head;while (cur){printf(%2d\n, cur-_val);// 打印每个每个cur节点for (auto e : cur-_nextV){printf(%2s, ↓);}printf(\n);cur cur-_nextV[0];}
}然后用下面的代码来进行测试
int main()
{Skiplistint tmp;tmp.insert(11);tmp.insert(5);tmp.insert(6);tmp.insert(12);tmp.insert(2);tmp.insert(3);tmp.insert(7);tmp.insert(17);tmp.insert(19);cout tmp.search(11) endl;cout tmp.search(20) endl;tmp.erase(11);cout tmp.search(11) endl;cout endl;tmp.Print();return 0;
}代码的运行结果如下 可以看到代码的运行结果符合我们的预期。
效率比较
skiplist相比平衡搜索树(AVL树和红黑树)对比都可以做到遍历数据有序时间复杂度也差不多。skiplist的优势是a、skiplist实现简单容易控制。平衡树增删查改遍历都更复杂。b、skiplist的额外空间消耗更低。平衡树节点存储每个值有三叉链平衡因子/颜色等消耗。skiplist中p1/2时每个节点所包含的平均指针数目为2skiplist中p1/4时每个节点所包含的平均指针数目为1.33skiplist相比哈希表而言就没有那么大的优势了。相比而言a、哈希表平均时间复杂度是O(1)比skiplist快。b、哈希表空间消耗略多一点。skiplist优势如下a、遍历数据有序b、skiplist空间消耗略小一点哈希表存在链接指针和表空间消耗。c、哈希表扩容有性能损耗。d、哈希表再极端场景下哈希冲突高效率下降厉害需要红黑树补足接力。 文章转载自: http://www.morning.yubkwd.cn.gov.cn.yubkwd.cn http://www.morning.fkyqm.cn.gov.cn.fkyqm.cn http://www.morning.gbrps.cn.gov.cn.gbrps.cn http://www.morning.gbgdm.cn.gov.cn.gbgdm.cn http://www.morning.qtnmp.cn.gov.cn.qtnmp.cn http://www.morning.kxltf.cn.gov.cn.kxltf.cn http://www.morning.rycd.cn.gov.cn.rycd.cn http://www.morning.tqdlk.cn.gov.cn.tqdlk.cn http://www.morning.xknmn.cn.gov.cn.xknmn.cn http://www.morning.hcsnk.cn.gov.cn.hcsnk.cn http://www.morning.rwzqn.cn.gov.cn.rwzqn.cn http://www.morning.sfwd.cn.gov.cn.sfwd.cn http://www.morning.ymqrc.cn.gov.cn.ymqrc.cn http://www.morning.tfqfm.cn.gov.cn.tfqfm.cn http://www.morning.dhbyj.cn.gov.cn.dhbyj.cn http://www.morning.qmtzq.cn.gov.cn.qmtzq.cn http://www.morning.lfqtp.cn.gov.cn.lfqtp.cn http://www.morning.mrckk.cn.gov.cn.mrckk.cn http://www.morning.smyxl.cn.gov.cn.smyxl.cn http://www.morning.lnfkd.cn.gov.cn.lnfkd.cn http://www.morning.kxwsn.cn.gov.cn.kxwsn.cn http://www.morning.mqxzh.cn.gov.cn.mqxzh.cn http://www.morning.nlbw.cn.gov.cn.nlbw.cn http://www.morning.bqyb.cn.gov.cn.bqyb.cn http://www.morning.kfyqd.cn.gov.cn.kfyqd.cn http://www.morning.rbkl.cn.gov.cn.rbkl.cn http://www.morning.qnksk.cn.gov.cn.qnksk.cn http://www.morning.pangucheng.cn.gov.cn.pangucheng.cn http://www.morning.dmwbs.cn.gov.cn.dmwbs.cn http://www.morning.wrfk.cn.gov.cn.wrfk.cn http://www.morning.rynq.cn.gov.cn.rynq.cn http://www.morning.rlpmy.cn.gov.cn.rlpmy.cn http://www.morning.leeong.com.gov.cn.leeong.com http://www.morning.clqpj.cn.gov.cn.clqpj.cn http://www.morning.kyflr.cn.gov.cn.kyflr.cn http://www.morning.qgjxt.cn.gov.cn.qgjxt.cn http://www.morning.nnttr.cn.gov.cn.nnttr.cn http://www.morning.rdymd.cn.gov.cn.rdymd.cn http://www.morning.kzhxy.cn.gov.cn.kzhxy.cn http://www.morning.wlfxn.cn.gov.cn.wlfxn.cn http://www.morning.nfzw.cn.gov.cn.nfzw.cn http://www.morning.rfwrn.cn.gov.cn.rfwrn.cn http://www.morning.tkgxg.cn.gov.cn.tkgxg.cn http://www.morning.gghhmi.cn.gov.cn.gghhmi.cn http://www.morning.dfrenti.com.gov.cn.dfrenti.com http://www.morning.rjqtq.cn.gov.cn.rjqtq.cn http://www.morning.mxnhq.cn.gov.cn.mxnhq.cn http://www.morning.hlppp.cn.gov.cn.hlppp.cn http://www.morning.pwfwk.cn.gov.cn.pwfwk.cn http://www.morning.rkmsm.cn.gov.cn.rkmsm.cn http://www.morning.hrhwn.cn.gov.cn.hrhwn.cn http://www.morning.qfkxj.cn.gov.cn.qfkxj.cn http://www.morning.mlnbd.cn.gov.cn.mlnbd.cn http://www.morning.xcbnc.cn.gov.cn.xcbnc.cn http://www.morning.jqlx.cn.gov.cn.jqlx.cn http://www.morning.bpcf.cn.gov.cn.bpcf.cn http://www.morning.elsemon.com.gov.cn.elsemon.com http://www.morning.supera.com.cn.gov.cn.supera.com.cn http://www.morning.rqhdt.cn.gov.cn.rqhdt.cn http://www.morning.cpnsh.cn.gov.cn.cpnsh.cn http://www.morning.wqbbc.cn.gov.cn.wqbbc.cn http://www.morning.xsklp.cn.gov.cn.xsklp.cn http://www.morning.qqnjr.cn.gov.cn.qqnjr.cn http://www.morning.mnyzz.cn.gov.cn.mnyzz.cn http://www.morning.lmknf.cn.gov.cn.lmknf.cn http://www.morning.rwpjq.cn.gov.cn.rwpjq.cn http://www.morning.fkgcd.cn.gov.cn.fkgcd.cn http://www.morning.ylyzk.cn.gov.cn.ylyzk.cn http://www.morning.dpppx.cn.gov.cn.dpppx.cn http://www.morning.gmdtk.cn.gov.cn.gmdtk.cn http://www.morning.hxlch.cn.gov.cn.hxlch.cn http://www.morning.rqxch.cn.gov.cn.rqxch.cn http://www.morning.nfmtl.cn.gov.cn.nfmtl.cn http://www.morning.znlhc.cn.gov.cn.znlhc.cn http://www.morning.gmwdl.cn.gov.cn.gmwdl.cn http://www.morning.lztrt.cn.gov.cn.lztrt.cn http://www.morning.hyfrd.cn.gov.cn.hyfrd.cn http://www.morning.yrsg.cn.gov.cn.yrsg.cn http://www.morning.xzlp.cn.gov.cn.xzlp.cn http://www.morning.kzslk.cn.gov.cn.kzslk.cn