資料結構與演算法學習--跳錶
跳錶
Skip List是一種隨機化的資料結構,基於並聯的連結串列,其效率可比擬於二叉查詢樹(對於大多數操作需要O(log n)平均時間)。基本上,跳躍列表是對有序的連結串列增加上附加的前進連結,增加是以隨機化的方式進行的,所以在列表中的查詢可以快速的跳過部分列表(因此得名)。所有操作都以對數隨機化的時間進行。Skip List可以很好解決有序連結串列查詢特定值的困難。
跳錶的定義及如何構造跳錶
構造一個有序的單鏈表,我們查詢某個元素,平均時間複雜度0(n)。
Skip List構造步驟:
為一個值有序的連結串列建立多級索引,比如每2個節點提取一個節點到上一級,我們把抽出來的那一級叫做索引或索引層。如下圖所示,其中down表示down指標,指向下一級節點。以此類推,對於節點數為n的連結串列,大約可以建立log2n-1級索引。像這種為連結串列建立多級索引的資料結構就稱為跳錶。
二、跳錶的時間複雜度?
1.計算跳錶的高度
如果連結串列有n個節點,每2個節點抽取抽出一個節點作為上一級索引的節點,那第1級索引的節點個數大約是n/2,第2級索引的節點個數大約是n/4,依次類推,第k級索引的節點個數就是n/(2k)。假設索引有h級別,最高階的索引有2個節點,則有n/(2h)=2,得出h=log2n-1,包含原始連結串列這一層,整個跳錶的高度就是log2n。
2.計算跳錶的時間複雜度
假設我們在跳錶中查詢某個資料的時候,如果每一層都遍歷m個節點,那在跳錶中查詢一個數據的時間複雜度就是O(m*logn)。那這個m是多少呢?如下圖所示,假設我們要查詢的資料是x,在第k級索引中,我們遍歷到y節點之後,發現x大於y,小於後面的節點z,所以我們通過y的down指標,從第k級下降到第k-1級索引。在第k-1級索引中,y和z之間只有3個節點(包含y和z),所以,我們在k-1級索引中最多隻需要遍歷3個節點,以此類推,每一級索引都最多隻需要遍歷3個節點。所以m=3。因此在跳錶中查詢某個資料的時間複雜度就是O(logn)。
三、跳錶的空間複雜度及如何優化?
1.計算索引的節點總數
如果連結串列有n個節點,每2個節點抽取抽出一個節點作為上一級索引的節點,那每一級索引的節點數分別為:n/2,n/4,n/8,…,8,4,2,等比數列求和n-1,所以跳錶的空間複雜度為O(n)。
2.如何優化時間複雜度
如果連結串列有n個節點,每3或5個節點抽取抽出一個節點作為上一級索引的節點,那每一級索引的節點數分別為(以3為例):n/3,n/9,n/27,…,27,9,3,1,等比數列求和n/2,所以跳錶的空間複雜度為O(n),和每2個節點抽取一次相比,時間複雜度要低不少呢。
四、高效的動態插入和刪除?
跳錶本質上就是連結串列,所以僅插作,插入和刪除操時間複雜度就為O(1),但在實際情況中,要插入或刪除某個節點,需要先查詢到指定位置,而這個查詢操作比較費時,但在跳錶中這個查詢操作的時間複雜度是O(logn),所以,跳錶的插入和刪除操作的是時間複雜度也是O(logn)。
五、跳錶索引動態更新?
當往跳錶中插入資料的時候,可以選擇同時將這個資料插入到部分索引層中,那麼如何選擇這個索引層呢?可以通過隨機函式來決定將這個節點插入到哪幾級索引中,比如隨機函式生成了值K,那就可以把這個節點新增到第1級到第K級索引中。
下面是跳錶的C程式碼的實現及簡單的除錯。
/*************************************************************************
> File Name: skiplist.h
> Author: jinshaohui
> Mail: [email protected]
> Time: 18-10-31
> Desc:
************************************************************************/
#ifndef __SKIP_LIST_H__
#define __SKIP_LIST_H__
typedef struct _node
{
int key; /*key是唯一的*/
int value; /*儲存的內容*/
int max_level; /*當前節點最大層數*/
struct _node *next[0];/*level層連結串列結構*/
}node;
typedef struct _skiplist
{
int level;
int count;
node *head;
}skiplist;
/*根據當前結構體元素的地址,獲取到結構體首地址*/
#define offsetof(TYPE,MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#define container(ptr,type,member) ({\
const typeof( ((type *)0)->member) *__mptr = (ptr);\
(type *) ( (char *)__mptr - offsetof(type,member));})
#endif
/*************************************************************************
> File Name: skiplist.c
> Author: jinshaohui
> Mail: [email protected]
> Time: 18-10-31
> Desc:
************************************************************************/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<assert.h>
#include"./skiplist.h"
/*建立node節點*/
node* skip_list_create_node(int level,int key,int value)
{
node * tmp = NULL;
tmp =(node *)malloc(sizeof(node) + level*sizeof(node *));
assert(tmp != NULL);
memset(tmp,0,sizeof(node) + level*sizeof(node*));
tmp->key = key;
tmp->value = value;
tmp->max_level = level;
return tmp;
}
/*建立跳錶的表頭,max_level層數*/
skiplist * skip_list_create(int max_level)
{
int i = 0;
skiplist * list = NULL;
list = (skiplist *)malloc (sizeof(skiplist));
assert(list != NULL);
list->level = 1;
list->count = 0;
list->head = skip_list_create_node(max_level,0,0);
if(list->head == NULL)
{
free(list);
return NULL;
}
return list;
}
/*skiplist 銷燬*/
void skip_list_destory(skiplist * list)
{
int i = 0;
node * tmp = NULL;
if((list == NULL) || (list->head == NULL))
{
return;
}
while(list->head->next[0] != NULL)
{
tmp = list->head->next[0];
list->head->next[0] = tmp->next[0];
free(tmp);
}
free(list->head);
free(list);
return;
}
/*插入元素獲得層數,是隨機產生的*/
int skip_list_level(skiplist * list)
{
int i = 0;
int level = 1;
for (i = 1; i < list->head->max_level; i++)
{
if ((rand()%2) == 1)
{
level++;
}
}
return level;
}
int skip_list_insert(skiplist *list,int key,int value)
{
int i = 0;
int level = 0;
node **update = NULL;/*用來更新每層的指標*/
node *tmp = NULL;
node *prev = NULL;
if (list == NULL)
{
return 1;
}
/*申請update空間用於儲存每層的指標*/
update = (node **)malloc(sizeof(node *)*list->head->max_level);
if (update == NULL)
{
return 2;
}
/*逐層查詢節點的*/
prev = list->head;
for (i = (list->level -1); i >= 0; i--)
{
/*初始化每level層的頭指標*/
while(((tmp = prev->next[i]) != NULL) && (tmp->key < key))
{
prev = tmp;
}
update[i] = prev;
}
/*當前key已經存在,返回錯誤*/
if ((tmp!= NULL) && (tmp->key == key))
{
return 3;
}
/*獲取插入元素的隨機層數,並更新跳錶的最大層數*/
level = skip_list_level(list);
/*建立當前資料節點*/
tmp = skip_list_create_node(level,key,value);
if (tmp == NULL)
{
return 4;
}
/*更新最大層數*/
if (level > list->level)
{
for (i = list->level;i < level; i ++)
{
update[i] = list->head;
}
list->level = level;
}
/*逐層更新節點的指標*/
for(i = 0; i < level; i++)
{
tmp->next[i] = update[i]->next[i];
update[i]->next[i] = tmp;
}
list->count++;
return 0;
}
int skip_list_delete(skiplist * list, int key ,int *value)
{
int i = 0;
node **update = NULL;/*用來更新每層的指標*/
node *tmp = NULL;
node *prev = NULL;
if ((list == NULL) && (value == NULL)&& (list->count == 0))
{
return 1;
}
/*申請update空間用於儲存每層的指標*/
update = (node **)malloc(sizeof(node *)*list->level);
if (update == NULL)
{
return 2;
}
/*逐層查詢節點的*/
prev = list->head;
for (i = (list->level -1); i >= 0; i--)
{
/*初始化每level層的頭指標*/
while(((tmp = prev->next[i]) != NULL) && (tmp->key < key))
{
prev = tmp;
}
update[i] = prev;
}
if ((tmp != NULL)
&& (tmp->key == key))
{
*value = tmp->value;
/*逐層刪除*/
for(i = 0; i < list->level; i++)
{
if(update[i]->next[i] == tmp)
{
update[i]->next[i] = tmp->next[i];
}
}
free(tmp);
tmp = NULL;
/*更新level的層數*/
for (i = list->level - 1; i >= 0; i++)
{
if (list->head->next[i] == NULL )
{
list->level--;
}
else
{
break;
}
}
list->count--;
}
else
{
return 3;/*未找到節點*/
}
return 0 ;
}
/*查詢當前key是否在跳錶中,如果存在返回查詢的value數值,不存在返回-1*/
int skip_list_search(skiplist *list,int key,int *value)
{
int i = 0;
node *prev = NULL;
node *tmp = NULL;
if((list == NULL) || (list->count == 0) || (value == NULL))
{
return 1;
}
prev = list->head;
for(i = list->level - 1; i >= 0; i--)
{
while(((tmp = prev->next[i]) != NULL) && (tmp->key <= key))
{
if (tmp->key == key)
{
*value = tmp->value;
return 0;
}
prev = tmp;
}
}
return -1;
}
void skip_list_dump(skiplist *list)
{
int i = 0;
node *ptmp = NULL;
printf("\r\n----------------------------------------------");
printf("\r\n skip list level[%d],count[%d]",list->level,list->count);
for(i = list->level - 1; i >= 0; i --)
{
ptmp = list->head->next[i];
printf("\r\n level[%d]:",i);
while(ptmp != NULL)
{
printf("%d-%d ",ptmp->key,ptmp->value);
ptmp = ptmp->next[i];
}
}
printf("\r\n----------------------------------------------");
return;
}
int main()
{
int res = 0;
int key = 0;
int value = 0;
skiplist *list = NULL;
list = skip_list_create(5);
assert(list != NULL);
while(1)
{
printf("\r\n 請輸入key 和 value,當key = 1000時,退出輸入:");
scanf("%d%d",&key,&value);
if (key == 1000)
{
break;
}
res = skip_list_insert(list,key,value);
if (res != 0)
{
printf("\r\n skip list insert %d,failed,res=%d.",key,res);
}
}
skip_list_dump(list);
while(1)
{
printf("\r\n 通過key 查詢value的數值,當key = 1000時,退出查詢");
scanf("%d",&key);
if(key == 1000)
{
break;
}
res = skip_list_search(list,key,&value);
if (res != 0)
{
printf("\r\n skip list search %d,failed,res=%d.",key,res);
}
else
{
printf("\r\n skip list search %d,sucessful,value=%d.",key,value);
}
}
skip_list_dump(list);
while(1)
{
printf("\r\n 通過key 刪除節點,當key = 1000時,退出刪除");
scanf("%d",&key);
if(key == 1000)
{
break;
}
res = skip_list_delete(list,key,&value);
if (res != 0)
{
printf("\r\n skip list search %d,failed,res=%d.",key,res);
}
else
{
printf("\r\n skip list search %d,sucessful,value=%d.",key,value);
}
}
skip_list_dump(list);
skip_list_destory(list);
return 0;
}