C语言 和 随机数原理 实现的跳跃链表
跳跃链表是数组二分查找的一种扩展形式,平均查找时间复杂度等价于二分查找,由能够动态的插入元素 解决了二分查找数组无法高效添加元素的问题
// 跳跃链表可以有多级指针这里简单的用 3 级指针来讲
level 3->-------------------<-->-----------------------<-->-----------------<-
level 2->-------------------<-->------------<-->-------<-->-----------------<-
level 1->-----------<-->----<-->----<-->----<-->-------<-->------<-->-------<-
data beforeFirst 5 7 23 25 30 35 afterLast
// beforeFirst 和 afterLast 作为辅助节点 各有3级指针
// 5 有 1 级指针
// 7 有 3 级指针
// 23 有 1 级指针
// 25 有 2 级指针
// 30 有 3 级指针
// 35 有 1 级指针
//
// 每一个节点都可以通过左边指针找到前一个元素,右边指针找到后一个元素
//
// 查找 35 不用像链表一样一个一个向后查找,可以通过高等级的指针跳跃查找
// 就比如 查找 35 不用从 5 一个一个查找到最后,
// 只需通过 beforeFirst 的第三级指针找到 7,再从 7 的第三级指针找到 30,再从 30 的第一级指针找到 35
//
//
// 节点的指针级数是通过随机数生成的,一级指针占数组的一半,二级指针占一级指针的一半, 三级指针占二级指针的一半,以此类推
// 因此具查找效率 接近于 二分查找
//
//
// 跳跃链表删除 25 只需 将节点23的一级指针指向30, 把30的一级指针指向23,
// 再将节点7的二级指针指向30, 把30的二级指针指向7
// 然后释放节点内存
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
typedef int bool;
typedef struct Node {
int level;
struct Node ** front;
struct Node ** back;
int data;
}Node;
typedef struct JumpList {
struct Node * beforeFirst;// 第一个元素的前一个元素
struct Node * afterLast; // 最后一个元素的后一个元素
}List;
#define MAX_LEVEL 5
#define MAX 9999
unsigned short GetLevel() {
unsigned short level = 1;
unsigned long randomN = (unsigned long)(rand() % MAX);
double n = MAX / 2.0;
while (randomN < n && level < MAX_LEVEL) {
n /= 2.0;
++level;
}
return level;
}
struct Node * MakeNode(int level) {
struct Node * node = (struct Node*)malloc(sizeof(struct Node));
node->front = (struct Node**)malloc(level*sizeof(struct Node*));
node->back = (struct Node**)malloc(level*sizeof(struct Node*));
node->level = level;
return node;
}
void DestroyNode(struct Node * node) {
free(node->front); // delete[] node->front
free(node->back); // delete[] node->back
free(node); // delete node
}
// 使用链表前必须 调用Init
void Init(struct JumpList * list) {
srand((unsigned)time(0));
list->beforeFirst = MakeNode(MAX_LEVEL+1);
list->afterLast = MakeNode(MAX_LEVEL+1);
for(int i=0; i<MAX_LEVEL; i++) {
list->beforeFirst->front[i] = NULL;
list->beforeFirst->back[i] = list->afterLast;
list->afterLast->front[i] = list->beforeFirst;
list->afterLast->back[i] = NULL;
}
}
// 使用完需要使用Destroy将内存释放
void Destroy(struct JumpList * list) {
struct Node* back;
for(struct Node* p=list->beforeFirst; p != list->afterLast; p=back) {
back = p->back[0];
DestroyNode(p);
}
DestroyNode(list->afterLast);
}
bool IsEmpty(struct JumpList * list) {
return list->beforeFirst->back[0] == list->afterLast;
}
// 在节点 pos 后插入一个节点 数据为data
void InsertNodeAfter(struct Node* pos, int data) {
struct Node * node = MakeNode(GetLevel());
node->data = data;
struct Node * pre = pos;
struct Node * nex = pos->back[0];
for(int i=0; i<node->level; i++) {
node->front[i] = pre;
node->back[i] = nex;
pre->back[i] = node;
nex->front[i] = node;
while (pre->level < i+2)
pre = pre->front[i];
while (nex->level < i+2)
nex = nex->back[i];
}
}
// 删除 list 中 pos 节点 (end 前一个元素是最后一个元素,不能删除 end 因为 end 是辅助节点不用于存储)
void RemoveNode(struct Node* pos) {
struct Node * pre, * nex;
for(int i=0; i<pos->level; i++) {
pre = pos->front[i];
nex = pos->back[i];
pre->back[i] = nex;
nex->front[i] = pre;
}
DestroyNode(pos);
}
// 在链表中从头到尾 查找第一个数据为data 的节点
struct Node* SearchList(struct JumpList * list, int data) {
int i = MAX_LEVEL-1;
struct Node * node = list->beforeFirst;
while (i >= 0) {
if(node->back[i] == list->afterLast) {
--i;
} else if(data > node->back[i]->data) {
--i;
} else if (node->data == data) {
break;
} else {
node = node->back[i];
}
}
return node;
}
void PrintList(struct JumpList * list) {
for(struct Node* p=list->beforeFirst->back[0]; p != list->afterLast; p=p->back[0]) {
printf("%d ", p->data);
}
printf("\n");
}
int main(void)
{
List list;
Init(&list);
printf("is empty %d\n",IsEmpty(&list));
InsertNodeAfter(list.beforeFirst, 1);
InsertNodeAfter(list.beforeFirst, 2);
InsertNodeAfter(list.beforeFirst, 3);
InsertNodeAfter(list.beforeFirst, 4);
InsertNodeAfter(list.beforeFirst, 5);
struct Node * searchResult = SearchList(&list, 1);
printf("search %d\n", searchResult->data);
RemoveNode(searchResult);
PrintList(&list);
Destroy(&list);
return 0;
}