1. 程式人生 > >資料結構與演算法學習--跳錶

資料結構與演算法學習--跳錶

跳錶

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; }