www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當前位置:首頁 > > 充電吧
[導讀]一.skiplist簡介跳表是由William Pugh發(fā)明。他在 Communications of the ACM June 1990, 33(6) 668-676 發(fā)表了Skip lists:

一.skiplist簡介
跳表是由William Pugh發(fā)明。他在 Communications of the ACM June 1990, 33(6) 668-676 發(fā)表了Skip lists: a probabilistic alternative to balanced trees,在該論文中詳細解釋了跳表的數(shù)據(jù)結(jié)構(gòu)和插入刪除操作。跳躍表使用概率均衡技術(shù)而不是使用強制性均衡,因此,對于插入和刪除結(jié)點比傳統(tǒng)上的平衡樹算法更為簡潔高效。參考算法導論,跳表插入、刪除、查找的復雜度均為O(logN)。LevelDB的核心數(shù)據(jù)結(jié)構(gòu)是用跳表實現(xiàn)的,redis的sorted set數(shù)據(jù)結(jié)構(gòu)也是用跳表實現(xiàn)的。
下面來研究一下跳表的核心思想:

先從鏈表開始,如果是一個單鏈表,那么我們知道在鏈表中查找一個元素I的話,需要將整個鏈表遍歷一次,時間復雜度為O(n)。


如果是說鏈表是排序的,并且結(jié)點中還存儲了指向后面第二個結(jié)點的指針的話,那么在查找一個結(jié)點時,僅僅需要遍歷N/2個結(jié)點即可。因為我們可以先通過每個結(jié)點最上面的指針先進行查找,這樣子就能跳過一半的節(jié)點。比如我們想查找19,首先和6比較,大于6之后,在和9進行比較,然后在和12進行比較......最后比較到21的時候,發(fā)現(xiàn)21大于19,說明查找的點在17和21之間,從這個過程中,我們可以看出,查找的時候跳過了3、7、12等點,因此查找的時間復雜度為O(n/2)(只是下圖的情況)。


其實,上面基本上就是跳躍表的思想,每一個結(jié)點不單單只包含指向下一個結(jié)點的指針,可能包含很多個指向后續(xù)結(jié)點的指針,這樣就可以跳過一些不必要的結(jié)點,從而加快查找、刪除等操作。對于一個鏈表內(nèi)每一個結(jié)點包含多少個指向后續(xù)元素的指針,這個過程是通過一個隨機函數(shù)生成器得到,這樣子就構(gòu)成了一個跳躍表。這就是為什么論文“Skip Lists : A Probabilistic Alternative to Balanced Trees ”中有“概率”的原因了,就是通過隨機生成一個結(jié)點中指向后續(xù)結(jié)點的指針數(shù)目。隨機生成的跳躍表可能如下圖所示。


如果一個結(jié)點存在k個指向后續(xù)元素指針的話,那么稱該結(jié)點是一個k層結(jié)點。一個跳表的層MaxLevel義為跳表中所有結(jié)點中最大的層數(shù)。跳表的所有操作均從上向下逐層進行,越上層一次next操作的跨度越大。

二.skiplist原理

為了描述方便,將跳表結(jié)構(gòu)繪畫成如下形式。


跳表結(jié)構(gòu):

(1) 由多層結(jié)構(gòu)組成
(2) 每一層都是一個有序的鏈表
(3) 最底層(Level 1)的鏈表包含所有元素
(4) 如果一個元素出現(xiàn)在 Level i 的鏈表中,則它在 Level i 之下的鏈表也都會出現(xiàn)。
(5) 每個節(jié)點包含兩個指針,一個指向同一鏈表中的下一個元素,一個指向下面一層的元素。

1.跳表的查找


例子:查找元素 117
(1) 比較 21, 比 21 大,往后面找
(2) 比較 37, ? 比 37大,比鏈表最大值小,從 37 的下面一層開始找
(3) 比較 71, ?比 71 大,比鏈表最大值小,從 71 的下面一層開始找
(4) 比較 85, 比 85 大,從后面找
(5) 比較 117, 等于 117, 找到了節(jié)點。


2.跳表的插入

1)K小于鏈表的層數(shù)

先確定該元素要占據(jù)的層數(shù) K(采用丟硬幣的方式,這完全是隨機的),然后在 Level 1 ... Level K 各個層的鏈表都插入元素。
例子:插入 119, K = 2


2)K大于鏈表的層數(shù)

如果 K 大于鏈表的層數(shù),則要添加新的層。
例子:插入 119, K = 4


插入元素的時候,元素所占有的層數(shù)完全是隨機的,通過某種隨機算法產(chǎn)生。

3.跳表的刪除

在各個層中找到包含 x 的節(jié)點,使用標準的 delete from list 方法刪除該節(jié)點。

例子:刪除 71


三.簡單的skiplist實現(xiàn)


#include#include#define?MAX_LEVEL?10?//最大層數(shù)??

//結(jié)點
typedef??struct?nodeStructure
{
	int?key;
	int?value;
	//指針數(shù)組
	//定義?int?*p[n];[]優(yōu)先級高,先與p結(jié)合成為一個數(shù)組,再由int*說明這是一個整型指針數(shù)組,它有n個指針類型的數(shù)組元素。
	//這里執(zhí)行p?+?1時,則p指向下一個數(shù)組元素,這樣賦值是錯誤的:p?=?a;因為p是個不可知的表示,只存在p[0]、p[1]、p[2]
	//...p[n?-?1],?而且它們分別是指針變量可以用來存放變量地址。但可以這樣?*p?=?a;?這里*p表示指針數(shù)組第一個元素的值,
	//即a的首地址的值。
	//這里定義了一個變長指針數(shù)組,數(shù)組中默認只有一個元素nodeStructure*。使用變長數(shù)組(柔性數(shù)組)的目的是為了后續(xù)對
	//該數(shù)組擴容,達到動態(tài)分配內(nèi)存,節(jié)約空間的目的。
	struct?nodeStructure?*next[1];
}nodeStructure;

//跳表??
typedef??struct?skiplist
{
	int?level;
	nodeStructure?*header;
}skiplist;

//創(chuàng)建結(jié)點?
nodeStructure*?createNode(int?level,?int?key,?int?value)
{
	//?nodeStructure::next是一個變長數(shù)組,額外分配時為什么是level-1倍呢???
	//?因為這里是為了兼容性變長數(shù)組聲明為struct?nodeStructure?*next[1];(也可以用next[0],但是有些編譯器不支持)??
	//?所以nodeStructure自身已經(jīng)占了一個,再添加level-1個即可,如果是next[0]的話,就不能減一了。??
	nodeStructure?*ns?=?(nodeStructure?*)malloc(sizeof(nodeStructure)?+?(level-1)*sizeof(nodeStructure*));
	ns->key?=?key;
	ns->value?=?value;
	return?ns;
}

//初始化跳表??
skiplist*?createSkiplist()
{
	skiplist?*sl?=?(skiplist?*)malloc(sizeof(skiplist));
	sl->level?=?0;
	sl->header?=?createNode(MAX_LEVEL?,?0,?0);
	for?(int?i?=?0;?i?<?MAX_LEVEL;?i++)
	{
		sl->header->next[i]?=?NULL;
	}
	return?sl;
}

//隨機產(chǎn)生層數(shù)??
int?randomLevel()
{
	int?k?=?1;
	while?(rand()?%?2)
		k++;
	k?=?(k?<?MAX_LEVEL)???k?:?MAX_LEVEL;
	return?k;
}

//插入節(jié)點??
bool?insert(skiplist?*sl,?int?key,?int?value)
{
	nodeStructure?*update[MAX_LEVEL];
	nodeStructure?*p,?*q?=?NULL;
	p?=?sl->header;
	int?k?=?sl->level;
	//這里還是查找插入位置階段,并未開始插入
	//從最高層往下,每層都查找插入位置,當遍歷到該層的尾部(p->next[i]==NULL)
	//也沒有找到比key大的結(jié)點時,將該層的最后一個結(jié)點的指針放到update[i]中。
	//如果在某層中找到了比key大或等于key的結(jié)點時,將該結(jié)點之前的那個比key小的結(jié)點的指針
	//放到update[i]中。
	//這樣一來,參考(二.skiplist原理中的跳表插入章節(jié)圖),以插入119為例,因為此時k暫時是最大層數(shù),
	//update[i]存放的是37、71和117所在結(jié)點的指針。
	for?(int?i?=?k?-?1;?i?>=?0;?i--){
		while?((q?=?p->next[i])?&&?(q->key?<?key))
		{
			p?=?q;
		}
		update[i]?=?p;
	}
	//不能插入相同的key,這里排除掉了上述等于key的情況。
	//此時q是第一層中的某個結(jié)點。
	if?(q&&q->key?==?key)
	{
		return?false;
	}

	//產(chǎn)生一個隨機層數(shù)k??
	k?=?randomLevel();
	//更新跳表的level,這里假設(shè)k=sl->level+1,參考(二.skiplist原理中的跳表插入章節(jié)圖),以插入119為例,
	//update[3]應該直接等于新增那層頭結(jié)點的指針,就像下面代碼寫的那樣。
	if?(k?>?(sl->level))
	{
		for?(int?i?=?sl->level;?i?<?k;?i++){
			update[i]?=?sl->header;
		}
		sl->level?=?k;
	}
	//新建一個待插入結(jié)點q,從低到高一層層插入??
	q?=?createNode(k,?key,?value);
	//逐層更新結(jié)點的指針,和普通鏈表插入一樣??
	for?(int?i?=?0;?i?<?k;?i++)
	{
		q->next[i]?=?update[i]->next[i];
		update[i]->next[i]?=?q;
	}
	return?true;
}

//搜索指定key的value??
int?search(skiplist?*sl,?int?key)
{
	nodeStructure?*p,?*q?=?NULL;
	p?=?sl->header;
	//從最高層往下開始搜索?
	int?k?=?sl->level;
	for?(int?i?=?k?-?1;?i?>=?0;?i--){
		while?((q?=?p->next[i])?&&?(q->key?key?==?key)
			{
				return?q->value;
			}
			p?=?q;
		}
	}
	return?NULL;
}

//刪除指定的key??
bool?deleteSL(skiplist?*sl,?int?key)
{
	nodeStructure?*update[MAX_LEVEL];
	nodeStructure?*p,?*q?=?NULL;
	p?=?sl->header;
	//從最高層往下開始搜索??
	int?k?=?sl->level;
	for?(int?i?=?k?-?1;?i?>=?0;?i--){
		while?((q?=?p->next[i])?&&?(q->key?<?key))
		{
			p?=?q;
		}
		update[i]?=?p;
	}
	//這里和插入類似,只不過找到該結(jié)點后刪除
	if?(q&&q->key?==?key)
	{
		//逐層刪除,和普通鏈表刪除一樣??
		for?(int?i?=?0;?i?<?sl->level;?i++){
			if?(update[i]->next[i]?==?q){
				update[i]->next[i]?=?q->next[i];
			}
		}
		free(q);
		//如果某些層只有被刪除的這個結(jié)點,那么需要更新跳表的level??
		for?(int?i?=?sl->level?-?1;?i?>=?0;?i--){
			if?(sl->header->next[i]?==?NULL){
				sl->level--;
			}
		}
		return?true;
	}
	else
	{
		return?false;
	}
}

//釋放跳表,雖然每個結(jié)點分了很多層,單歸根結(jié)底是一個結(jié)點,所以和單鏈表的釋放是一樣的。
void?freeSL(skiplist?*sl)
{
	if?(!sl)
		return;
	nodeStructure?*p,?*q?=?NULL;
	p?=?sl->header;

	while?(p)
	{
		q?=?p->next[0];
		free(p);
		p?=?q;
	}
	free(sl);
}

void?printSL(skiplist?*sl)
{
	//從最高層開始打印??
	nodeStructure?*p,?*q?=?NULL;

	int?k?=?sl->level;
	for?(int?i?=?k?-?1;?i?>=?0;?i--)
	{
		p?=?sl->header;
		while?(1)
		{
			q?=?p->next[i];
			printf("%d?->?",?p->value);
			p?=?q;
			if?(p==NULL)
			{
				break;
			}
		}
		printf("n");
	}
	printf("n");
}
int?main()
{
	skiplist?*sl?=?createSkiplist();
	printf("插入結(jié)點:1<key=value<11n");
	for?(int?i?=?1;?i?<?11;?i++)
	{
		insert(sl,?i,?i);
	}
	printSL(sl);
	//搜索??
	printf("搜索key=3的valuen");
	int?i?=?search(sl,?3);
	printf("搜索結(jié)果value=%dn",i);
	//刪除??
	printf("刪除key=3的結(jié)點n");
	bool?b?=?deleteSL(sl,?3);
	if?(b)
	{
		printf("刪除成功n");
	}
	printSL(sl);
	freeSL(sl);
	system("pause");
	return?0;
}

運行結(jié)果如下,由于K值得隨機性,并不是每次的運行結(jié)果都是這樣。



本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

LED驅(qū)動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: 驅(qū)動電源

在工業(yè)自動化蓬勃發(fā)展的當下,工業(yè)電機作為核心動力設(shè)備,其驅(qū)動電源的性能直接關(guān)系到整個系統(tǒng)的穩(wěn)定性和可靠性。其中,反電動勢抑制與過流保護是驅(qū)動電源設(shè)計中至關(guān)重要的兩個環(huán)節(jié),集成化方案的設(shè)計成為提升電機驅(qū)動性能的關(guān)鍵。

關(guān)鍵字: 工業(yè)電機 驅(qū)動電源

LED 驅(qū)動電源作為 LED 照明系統(tǒng)的 “心臟”,其穩(wěn)定性直接決定了整個照明設(shè)備的使用壽命。然而,在實際應用中,LED 驅(qū)動電源易損壞的問題卻十分常見,不僅增加了維護成本,還影響了用戶體驗。要解決這一問題,需從設(shè)計、生...

關(guān)鍵字: 驅(qū)動電源 照明系統(tǒng) 散熱

根據(jù)LED驅(qū)動電源的公式,電感內(nèi)電流波動大小和電感值成反比,輸出紋波和輸出電容值成反比。所以加大電感值和輸出電容值可以減小紋波。

關(guān)鍵字: LED 設(shè)計 驅(qū)動電源

電動汽車(EV)作為新能源汽車的重要代表,正逐漸成為全球汽車產(chǎn)業(yè)的重要發(fā)展方向。電動汽車的核心技術(shù)之一是電機驅(qū)動控制系統(tǒng),而絕緣柵雙極型晶體管(IGBT)作為電機驅(qū)動系統(tǒng)中的關(guān)鍵元件,其性能直接影響到電動汽車的動力性能和...

關(guān)鍵字: 電動汽車 新能源 驅(qū)動電源

在現(xiàn)代城市建設(shè)中,街道及停車場照明作為基礎(chǔ)設(shè)施的重要組成部分,其質(zhì)量和效率直接關(guān)系到城市的公共安全、居民生活質(zhì)量和能源利用效率。隨著科技的進步,高亮度白光發(fā)光二極管(LED)因其獨特的優(yōu)勢逐漸取代傳統(tǒng)光源,成為大功率區(qū)域...

關(guān)鍵字: 發(fā)光二極管 驅(qū)動電源 LED

LED通用照明設(shè)計工程師會遇到許多挑戰(zhàn),如功率密度、功率因數(shù)校正(PFC)、空間受限和可靠性等。

關(guān)鍵字: LED 驅(qū)動電源 功率因數(shù)校正

在LED照明技術(shù)日益普及的今天,LED驅(qū)動電源的電磁干擾(EMI)問題成為了一個不可忽視的挑戰(zhàn)。電磁干擾不僅會影響LED燈具的正常工作,還可能對周圍電子設(shè)備造成不利影響,甚至引發(fā)系統(tǒng)故障。因此,采取有效的硬件措施來解決L...

關(guān)鍵字: LED照明技術(shù) 電磁干擾 驅(qū)動電源

開關(guān)電源具有效率高的特性,而且開關(guān)電源的變壓器體積比串聯(lián)穩(wěn)壓型電源的要小得多,電源電路比較整潔,整機重量也有所下降,所以,現(xiàn)在的LED驅(qū)動電源

關(guān)鍵字: LED 驅(qū)動電源 開關(guān)電源

LED驅(qū)動電源是把電源供應轉(zhuǎn)換為特定的電壓電流以驅(qū)動LED發(fā)光的電壓轉(zhuǎn)換器,通常情況下:LED驅(qū)動電源的輸入包括高壓工頻交流(即市電)、低壓直流、高壓直流、低壓高頻交流(如電子變壓器的輸出)等。

關(guān)鍵字: LED 隧道燈 驅(qū)動電源
關(guān)閉