让你的代码更加优雅的编程技巧跳转表
跳表比较好理解,但是实际用代码来表示,还是有点复杂的。
实现的方法不唯一 1. 什么是跳表
跳表是 链表 + 索引 的一种数据结构 ,是以空间换取时间的方式,关于跳表参考: https://baike.baidu.com/item/跳表/22819833?fr=aladdin2. 跳表概念
跳表在原有链表的基础上,增加索引,从而可以进行二分查找,提高搜寻效率。
原始链表Head ——> 1 ——> 8 ——> 12 ——> 23 ——> 55 ——> NULL
新增了索引的链表(跳表)Head2 ————————> 8 ———————————————————————> NULL Head1 ————————> 8 —————————> 23 —————————> NULL Head0 ——> 1 ——> 8 ——> 12 ——> 23 ——> 55 ——> NULL
Head0 , Head1 , Head2 上都是真实的节点,这就是以空间换取时间
例如算上Head, 元素数据一共有 6 个,而添加索引后,元素一共有 11 个3. 跳表增删查规则
3.1 跳表数据节点
数据节点可以和链表节点一致 ,也可以定义如下节点,除了数据外,有指针指向 前一个/后一个/上一个/下一个 节点,以便后续查找操作。typedef struct { int data; struct Node *next; // 后一个节点 struct Node *last; // 前一个节点 struct Node *up; // 上一个节点 struct Node *down; // 下一个节点 } Node;
3.2 跳表初始化
当跳表有多少层的时候,应当建立多少个头结点,例如: 跳表为3层Head2 ——> NULL Head1 ——> NULL Head0 ——> NULL 3.3 查找
删除/新增 都会进行查询才操作,无非是删除/新增索引而已。
例如有如下数据Head2 —————————————————————> 23 —————————> NULL Head1 ————————> 8 —————————> 23 —————————> NULL Head0 ——> 1 ——> 8 ——> 12 ——> 23 ——> 55 ——> NULL
要查找 13这个节点
去除无效层
例如: Head2 后面第一个节点的数据 23 , 而 23 大于 13 , 所以 Head2 没有数据匹配查询,故需要跳到下面一层,至 Head1 上进行查询。
查询至Head0层
去除无效层后数据进入了 Head1 , 在Head1上进行匹配,当匹配到 23 时,23大于13,将23标记为 查询结束点,对23的上一个节点 8 进行 向下指针操作,进入 Head0层的8节点。
查找实际数据
从Head0层的8 进行查找,直至 查询结束标记点(head1 23), 查询的数据分别为 8 , 12 ,23 查询结束,未找到数据。
嵌入式物联网需要学的东西真的非常多,千万不要学错了路线和内容,导致工资要不上去!
无偿分享大家一个资料包,差不多150多G。里面学习内容、面经、项目都比较新也比较全!某鱼上买估计至少要好几十。
点击这里找小助理0元领取:嵌入式物联网学习资料(头条)
3.4 新增
新增操作需要记录索引寻址过程,以便后续新增索引。
头结点插入
头结点插入一定是 去除无效层 至Head0 , 且 Head0的第一个节点都比插入节点要大的情况下
例如:
如下跳表,插入 2Head2 —————————————————————> 23 —————————> NULL Head1 ————————> 8 —————————> 23 —————————> NULL Head0 ——> 3 ——> 8 ——> 12 ——> 23 ——> 55 ——> NULL
尾结点插入
头结点插入一定是 去除无效层 至Head0 , 且 Head0的第一个节点都比插入节点要小,直至NULL节点的情况下
例如:
如下跳表,插入 65Head2 —————————————————————> 23 —————————> NULL Head1 ————————> 8 —————————> 23 —————————> NULL Head0 ——> 3 ——> 8 ——> 12 ——> 23 ——> 55 ——> NULL
中间节点插入
除开以上2种情况,其余情况为 中间节点插入
新增索引
抛硬币的方法,当数据量达到一定规模的时候,一定是趋近于 50%的。
所以跳表会越来越趋向于如下形式 3 3 7 1 3 5 7 9 1 2 3 4 5 6 7 8 9
判断是否需要新增索引,采取抛硬币的方法来判断,即: 随机数 取余 为 0 则需要新增,否则不需要。
例如如下跳表,插入 65Head2 —————————————————————> 23 —————————> NULL Head1 ————————> 8 —————————> 23 —————————> NULL Head0 ——> 3 ——> 8 ——> 12 ——> 23 ——> 55 ——> NULL
寻址应该为
Head2: 23
Head1: 23
元素数据插入后为Head2 —————————————————————> 23 ———————————————> NULL Head1 ————————> 8 —————————> 23 ———————————————> NULL Head0 ——> 3 ——> 8 ——> 12 ——> 23 ——> 55 ——> 65 —> NULL
当插入65节点后,若判断需要索引的时候,则先为 Head1 添加索引,添加位置为 寻址地址之后,寄 Head1: 23Head2 —————————————————————> 23 ———————————————> NULL Head1 ————————> 8 —————————> 23 —————————> 65 —> NULL Head0 ——> 3 ——> 8 ——> 12 ——> 23 ——> 55 ——> 65 —> NULL
继续判断,若不需要添加索引,则插入结束
若还需要添加索引,则继续上述操作,直至 索引层 达到最高层
3.5 删除
删除首先是查找操作【3.3 查找】
若未找到该节点,则删除失败
若找到了该节点,则应当提到该数据最高索引层,再从高到低删除
例如:
如下跳表,删除 23Head2 —————————————————————> 23 ———————————————> NULL Head1 ————————> 8 —————————> 23 —————————> 65 —> NULL Head0 ——> 3 ——> 8 ——> 12 ——> 23 ——> 55 ——> 65 —> NULL
找到 Head0 23 后,应该向上找到 Head2 23 ,然后从高向低删除,若删除后,该索引没有数据了,则索引层减1
则删除Head2 23 后数据如下Head1 ————————> 8 —————————> 23 —————————> 65 —> NULL Head0 ——> 3 ——> 8 ——> 12 ——> 23 ——> 55 ——> 65 —> NULL
删除Head1 23 后数据如下Head1 ————————> 8 ———————————————————————> 65 —> NULL Head0 ——> 3 ——> 8 ——> 12 ——> 23 ——> 55 ——> 65 —> NULL
删除Head0 23后数据如下Head1 ————————> 8 ————————————————> 65 —> NULL Head0 ——> 3 ——> 8 ——> 12 ——> 55 ——> 65 —> NULL 4. 代码
skipList.c# include # include # include int MaxLevel = 8; // 最大层数 int currLevel = 0; // 当前层数 // 数据节点 typedef struct { int data; struct Node *next; struct Node *last; struct Node *up; struct Node *down; } Node; // 记录索引寻址过程 typedef struct { int level; struct Node *node; } skipStep; // 判断是否需要新增索引, 抛硬币 bool randNum() { if(0 == (rand() % 2)) return true; return false; } // 新增节点 bool add(Node *SL[] , int data) { printf("新增节点: %d ",data); int level = currLevel; Node *Head = NULL; Node *tmp = NULL; Node *last = NULL; // 初始化索引 数据为 Head 地址 skipStep steps[MaxLevel]; int i; for (i=0;inext; while ((level > 0) && (data < tmp->data)) { level--; Head = SL[level]; tmp = Head->next; } // 根据索引寻找Head0数据节点 while ((level > 0)) { while (tmp != NULL) { if (data < tmp->data) { steps[level].level = level; if (NULL != last) steps[level].node = last; tmp = last->down; level--; break; } last = tmp; tmp = tmp->next; } if (NULL == tmp) { steps[level].level = level; if (NULL != last) steps[level].node = last; tmp = last->down; level--; } } // Head0 数据合适的节点 while (tmp != NULL) { if (data < tmp->data) { break; } last = tmp; tmp = tmp->next; } // 新增节点 Node *newData = (Node *)malloc(sizeof(Node)); newData->data = data; newData->up = NULL; newData->down = NULL; newData->last = NULL; newData->next = NULL; int k = 0; // Head0 插入原始数据 if (NULL == last ) { // 头结点 Head = SL[0]; Node *headNext = Head->next; if (NULL != headNext) { newData->next = headNext; headNext->last = newData; newData->last = Head; } Head->next = newData; newData->last = Head; } else if ( NULL == tmp) { // 尾节点 last->next = newData; newData->last = last; } else { // 中间节点 newData->next = tmp; tmp->last = newData; newData->last = last; last->next = newData; } // 构建索引 while (randNum()) { k++; if (k >= MaxLevel) break; // 新增索引数据 Node *newIndex = (Node *)malloc(sizeof(Node)); newIndex->data = data; newIndex->up = NULL; newIndex->down = NULL; newIndex->next = NULL; newIndex->last = NULL; // 建立上下级关系 newIndex->down = newData; newData->up = newIndex; Node *node = steps[k].node; // node->next Node *nextIndex = node->next; node->next = newIndex; newIndex->last = node; newIndex->next = nextIndex; if (NULL != nextIndex) nextIndex->last = newIndex; newData = newIndex; // 判断是否需要新增索引层数 if (k > currLevel) currLevel = k; } } // 初始化头结点 Node *initSkipList(Node *skipList[]) { int i; for (i=0;idata = -1-i; newHead->down = NULL; newHead->up = NULL; newHead->next = NULL; newHead->last = NULL; skipList[i] = newHead; } return skipList; } // 打印跳表数据 void PrintSkipList(Node *SL[]) { if (NULL == SL) { return; }; int level = currLevel; //int level = MaxLevel; int i; for (i=level;i>=0;i--) { Node *Head = SL[i]; Node *tmp = Head->next; printf("第%d层 ",i); while (NULL != tmp) { printf(" %d ",tmp->data); tmp = tmp->next; } printf(" "); } } // 查询数据 Node *query(Node *SL[] , int data) { printf("查询数据: %d ",data); int level = currLevel; Node *Head = NULL; Node *tmp = NULL; Node *last = NULL; Head = SL[level]; tmp = Head->next; int endQuery = -1; // 筛除无效层 while ((level > 0) && (data < tmp->data)) { level--; endQuery = tmp->data; Head = SL[level]; tmp = Head->next; } // 根据索引定位到Head0层 while ((level > 0 )) { while (tmp != NULL) { if (data < (tmp->data)) { level--; endQuery = tmp->data; tmp = last->down; break; } last = tmp; tmp = tmp->next; } if (NULL == tmp) { tmp = last->down; endQuery = -1; level--; } } // 查询实际数据 while (NULL != tmp) { if (endQuery != -1) if (tmp->data > endQuery) { tmp = NULL; break; } if (tmp->data == data) { break; } tmp = tmp->next; } // 返回查询的数据节点,若没有查询到,应当返回NULL ,否则返回实际的地址 return tmp; } // 删除数据 bool del(Node *SL[],int data) { printf("删除数据: %d ",data); // 找到节点地址 Node *tmp = query(SL,data); if (NULL == tmp) { printf("未找到节点,删除失败 "); return false; } int level = 0; Node *t_last = NULL; Node *t_next = NULL; // 找到该数据最高索引 while (NULL != tmp->up) { level++; tmp = tmp->up; } // 由上至下删除索引/数据 while (tmp != NULL) { t_last = tmp->last; t_next = tmp->next; Node *t_down = tmp->down; if (t_last == NULL) { printf("上一个节点不可能为空,删除失败,层数: %d ",level); return false; } t_last->next = t_next; if (NULL != t_next) t_next->last = t_last; else t_last->next = NULL; if ((t_last == SL[level]) && (NULL == t_next)) { currLevel--; } free(tmp); tmp = t_down; level--; } return true; } int main() { Node *SL[MaxLevel]; Node *skipList = initSkipList(SL); if (NULL == SL) { printf("skipList 申请失败 "); return -1; } // 测试新增 int num[] = {1,3,2,10,8,9,22,30,29,120,99,78,55,76,21}; int i; for (i=0;i
广州新规!二孩以上家庭,公积金房贷有调整12月9日,16届24次市政府常务会议审议通过了广州市人口与计划生育服务规定(简称规定),规定明确提出通过完善住房财政教育金融人才就业等支持政策,促进人口长期均衡发展,优化生育服务
英联杯曼联VS伯恩利球队近况分析曼联本赛季变化不小,目前八胜两平四负的战绩,位列联赛第五位。滕哈赫入主之后是将他在阿贾克斯的执教理念搬了过来,虽然阿贾克斯在联赛中也是一支崇尚进攻且充满压迫性的球队,但
曼彻斯特联VS伯恩利曼联曼联本赛季战绩不佳,目前排名第五,距离热刺只有三分之遥,C罗也离开了球队,而卡塞米罗马丁内斯埃里克森等球队也都是球队的主力,所以他们必须要拿到欧战区。在过去的两次热身赛中,他们
英联杯纽卡斯尔伯恩茅斯纽卡斯尔伯恩茅斯纽卡斯尔本赛季表现相当出色,凭借8胜6平1负的战绩,拿到30分暂列联赛第3的位置,目前也仅落后榜首阿森纳7分。纽卡斯尔联进攻端表现出色防守也相当稳固,是目前英超联赛
纽卡斯尔联VS伯恩茅斯这个赛季,纽卡的崛起和球队的不懈努力最终获得了成功。他们在15场联赛中的战绩是8胜6平1败。他们在联赛中打入29个进球,11个失球,目前积分榜上位列第3。纽卡斯尔最近没有什么大的赛
英联赛杯,纽卡斯尔联VS伯恩茅斯英联赛杯,纽卡斯尔联VS伯恩茅斯英联杯从196061赛季举办,到今年已经进行了62届,因为201617赛季开始卡拉宝(功能饮料)的赞助,所以这杯赛现在也叫卡拉宝杯。英联杯的参赛队伍
英联杯纽卡斯尔联VS伯恩茅斯除了夏季前锋艾萨克(3场2球)长期因伤缺阵外,边锋圣马克西曼(8场1球3助攻)和中场乔乔林顿(15场1球1助攻)也在世界杯友谊赛中受伤下场。前者小腿有问题,后者腿筋拉伤,能否参加比
英联杯情报南安普敦新帅主场首秀,林肯城主力阵容齐整沃克皮特斯归队,一名后卫缺席南安普敦右卫沃克皮特斯(本赛季11场比赛都是首发出场),英国媒体报导说,这场比赛他将会在这场比赛中恢复。另外,拉里奥斯(本赛季5场2次首发)也会在这场比
库兹马女友又来了!露一身白斑,NBA最另类花魁,拥抱奇才巨星NBA常规赛,奇才113110险胜太阳,终于结束了球队的10连败,此役,奇才当家球星库兹马发挥出色,他拿到29分6篮板6助攻,此外队友比尔也有不错的数据,27分6篮板6助攻,不得不
在鸿蒙智能座舱上,窥见智能汽车的下半场世界上本没有座舱。100多年前当卡尔本茨制造出世界上第一辆汽车时,别说舱了,连顶篷都没有,座椅发动机等部件全部暴露在外面,所谓的座舱,大概就是一把椅子。当然,在人们对汽车的要求不再
16连败!成为CBA唯一没有获胜的球队,网友滚出CBA宁波队输给深圳队11分,遭到16连败CBA常规赛第16轮,宁波队与深圳队的比赛,双方经过四节比拼,最终,深圳队依靠末节比赛发力,以10190赢了宁波队11分。如此一来,深圳队收获了