跳錶(skip list)的c++實現

首先聲明,是轉摘別人的代碼,本人稍作更改。在microsoft visual studio comunity 2017環境下測試通過。要理解跳錶是如何實現的,看懂這個圖很重要(摘自維基百科)

 

跳錶數據結構

A schematic picture of the skip list data structure. Each box with an arrow represents a pointer and a row is a linked list giving a sparse subsequence; the numbered boxes (in yellow) at the bottom represent the ordered data sequence. Searching proceeds downwards from the sparsest subsequence at the top until consecutive elements bracketing the search element are found

我翻譯一下:跳錶數據結構的示意圖。每個帶箭頭的箱子代表一個指針,每一行是是一個鏈表,每個鏈表給出一個稀疏子序列;底部的編號的箱子(黃色)表示有序的數據列。搜索過程由頂部的最稀疏的子序列開始,方向朝下,直到包圍搜索元素的兩個連續元素被發現。

由此可見:元素類型包含一個數據值(黃色箱子)和一個指針向量(用next表示),指針指向某個元素。因爲向量的成員也叫元素,爲了區別,下面我把鏈表的元素簡稱爲元素,把指針向量的元素稱爲指針。所有元素的指針是緊密排列的,這表明指針的個數(next.size())就是該元素出現在子序列中的次數。每個指針都水平向右指向,這表明子序列由相同下標的指針指向的元素組成,並且指針的下標編號和子序列的編號(從0開始編號,0號表示最底層,這一層包含所有元素)是相同的,因此我們可以很方便的通過指針向量的下標操作來訪問每一層子序列的元素。每個元素的指針個數由隨機數產生,最大max_height,擁有最大指針個數的元素決定了鏈表的高度。從圖形我們也可以很直觀的看出每一層子序列是一個單向有序鏈表,而且越高的子序列越稀疏。

搜索過程結束的條件是:直到p和p->next[0](p->key< x <=p->next[0]->key)這兩個元素被發現。p表示搜索經歷的元素,x表示搜索元素值,next[0]表示最底層子序列上p指向的下一個元素,如果x==p-next[0]->key,則p->next[0]就是要找的元素,否則數據列不存在x。

看懂了這個圖,基本上就能理解如何用c++來實現跳錶了。

// exercise18.11.cpp: 定義控制檯應用程序的入口點。
//

#include "stdafx.h"


// Chapter 18 exercise 11: implement a skip list. Following Ottmann & Widmayer,
// "Algorithmen and Datenstrukturen" (3rd Edition), 1997, Chapter 1.7.1

#include "d:/download/stroustrup-ppp-master/lib_files/std_lib_facilities.h"

struct element {
	element(int k, int height) : key(k), next(height) { }    //height:元素的層高
	int key;
	vector<element*> next;
};

class skip_list {
public:
	skip_list();
	element* find(int x);
	void insert(int x);
	void remove(int x);
	void print();
	void debug_print();
	void desc_print();
	int get_height() { return height; }
private:
	const int max_height = 4;
	const int infty = numeric_limits<int>::max();
	element head;
	element tail;
	int height;                        //有效層高,從0開始編號,所以實際有效層高數是height+1,可能的最大值是max_height-1
	int random_height();
};

// initialise empty list with head element's next pointing to tail element
// on all list levels
skip_list::skip_list()
	:head(0, max_height), tail(infty, 1), height(0)
{
	for (int i = 0; i<head.next.size(); ++i)
		head.next[i] = &tail;
}

// returns pointer to element with key x if x exists in list, 0 otherwise
element* skip_list::find(int x) {
	element* p = &head;

	for (int i = height; i >= 0; --i) {
		while (p->next[i]->key < x) {
			p = p->next[i];
		}
	}

	// now either p == &head and x <= p->next[0]->key
	// or p != &head and p->key < x <= p->next[0]->key
	p = p->next[0];
	if (p->key == x) return p;  // x is at position p in list
	else return 0;              // x is not in list
}

// inserts element with key x into list
void skip_list::insert(int x) {
	vector<element*> update(max_height);
	element* p = &head;

	for (int i = height; i >= 0; --i) {
		while (p->next[i]->key < x) {
			p = p->next[i];
		}
		update[i] = p;
	}
	p = p->next[0];

	if (p->key == x) return;    // key x exists already in list

	int new_height = random_height();
	if (new_height > height) {  // link new element to head, adjust list height
		for (int i = height + 1; i <= new_height; ++i) {
			update[i] = &head;
		}
		height = new_height;
	}

	// create new element with height new_height and key x
	p = new element(x, new_height+1);

	// insert p into level i lists immediately after element update[i]
	for (int i = 0; i <= new_height; ++i) {
		p->next[i] = update[i]->next[i];
		update[i]->next[i] = p;
	}
}

// removes element with key x from list
void skip_list::remove(int x) {
	vector<element*> update(max_height);
	element* p = &head;
	for (int i = height; i >= 0; --i) {
		while (p->next[i]->key < x)
			p = p->next[i];
		update[i] = p;
	}
	p = p->next[0];

	// if found, remove and potentially reduce list height
	if (p->key == x) {
		for (int i = 0; i<p->next.size(); ++i) {
			// remove p from level i list
			update[i]->next[i] = p->next[i];
		}
		while (height >= 1 && head.next[height]->key == infty)
			--height;
		delete p;
	}
}

void skip_list::print() {
	element* p = head.next[0];
	cout << "{";
	while (p->key != infty) {
		cout << ' ' << setw(2) << p->key;
		p = p->next[0];
	}
	cout << " }" << "\n";
}

// print lists at higher levels
void skip_list::debug_print() {
	for (int i = 0; i <= height; ++i) {
		element* p = head.next[0];
		cout << "Lvl " << i+1 << ": {";
		while (p->key != infty) {
			if (p->next.size() > i)
				cout << ' ' << setw(2) << p->key;
			else
				cout << "   ";
			p = p->next[0];
		}
		cout << " }" << "\n";
	}
}

void skip_list::desc_print() {
	for (int i = height; i >= 0; --i) {
		element* p = head.next[0];            //最底層才包含所有元素
		cout << "Lvl " << i+1 << ": {";
		while (p->key != infty) {
			if (p->next.size() > i)
				cout << ' ' << setw(2) << p->key;
			else
				cout << "   ";
			p = p->next[0];
		}
		cout << " }" << "\n";
	}
}

int skip_list::random_height() {
	int rand_height = 0;
	while (randint(10000)<5000 && rand_height<max_height-1)
		++rand_height;
	return rand_height;
}

int main()
try {
	srand(time(0));
	const int test_val = 75;
	while (true) {
		skip_list sl;
		for (int i = 0; i<23; ++i)
			sl.insert(randint(100));
		sl.desc_print();
		cout << "Enter value to remove: ";
		int x;
		cin >> x;
		if (x == -1) return 0;

		element* p = sl.find(x);
		sl.remove(x);
		sl.desc_print();
		cout << "\n";
	}
}
catch (exception& e) {
	cerr << "exception: " << e.what() << endl;
}
catch (...) {
	cerr << "exception\n";
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章