算法與數據結構【C++】:跳躍鏈表

         普通鏈表有一個嚴重的缺陷:查找某對象時需要遍歷整個鏈表,直到找到了該元素或者遍歷完了整個鏈表也沒有找到,時間複雜度很高。

         爲了解決該問題,可以使用跳躍鏈表。

跳躍鏈表的特點:

跳躍鏈表中的元素按照從小到達或從大到小的規則排列,該順序在向鏈表加入元素時維護,所以,只能指定向鏈表插入某個值,不能指定插入位置
跳躍鏈表使用了二分查找的思想, 查找某個元素的複雜度是O(logn)

跳躍鏈表最底層(對應代碼中的0層)包含了所有元素
跳躍鏈表第1層(對應代碼中的1層)包含了從0層中隨機抽取的一半的元素(理論上)
跳躍鏈表第2層(對應代碼中的2層)包含了從1層中隨機抽取的一半的元素(理論上)

以此類推

爲什麼使用隨機數?

因爲跳躍表在何處插入和刪除值未知,很難使用預定算法使得這個鏈表“平衡”(即上層索引較爲平均的分隔下層索引)。所以使用隨機數決定,要將新加入的結點存入哪些層

算法中的缺陷:

加入元素時採用隨機值決定要插入多少層;但是刪除時,必須一律從最高層開始刪除,多次添加和刪除之後,較高層的插入是根據隨機數,刪除是必須刪除,所以會發生較高層結點較少的情況,降低查找效率。

另外,算法根據隨機數進行層數分配,有一定的不可控因素。

        

#include<iostream>
#include<string>
#include<stdio.h>
#include <time.h> 
#include <stdlib.h> 
#include<cmath>
using namespace std;


//結點類
class Node{
	public:
	int value;		//該結點的值
	Node* next;		//該結點的下一個結點
	Node* down;		//該結點對應的下一層結點
	
	Node(int aValue, Node* aNext=0, Node* aDown=0){
		value = aValue;
		next = aNext;
		down = aDown;
	}
}; 

//順序表,鏈表中的元素按升序排列
class SortedList{
	public:
	Node* head;		//表頭結點
	Node* tail;		//表尾結點
	int length;		//鏈表長度
	
	SortedList(){
		head = tail = 0;
		length = 0;
	}
	
	
	//插入結點
	//參數:要插入新節點的值;要插入的結點的前一個結點的引用
	Node* insertNode(int value, Node* aheadOfInsert){

		//被插入的結點
		Node* InsertedNode = 0;

		//如果鏈表爲空,調用向頭部插入方法
		if (aheadOfInsert==0){
			InsertedNode = insertToHead(value);
		}
		//要在鏈表尾部插入,直接更新tail
		else if (aheadOfInsert == tail){
			InsertedNode = tail = aheadOfInsert->next = new Node(value, 0);
		}
		//在鏈表中間插入
		else {
			InsertedNode = aheadOfInsert->next = new Node(value, aheadOfInsert->next);
		}
		length++;
				
		return InsertedNode;
	}


	//刪除結點
	//參數:要刪除的結點的前一個結點的引用
	//如果要刪除第一個結點,則該參數爲空
	int deleteNode(Node* aheadOfRemove){
		//鏈表爲空,直接返回-1
		if (head == 0){
			return -1;
		}


		Node* removedNode = 0;
		//鏈表只有一個結點,刪除該結點
		if (head == tail){
			removedNode = head;
			head = tail = 0;
		}

		//參數爲空,代表要刪除第一個結點。同時更新head
		else if (aheadOfRemove == 0){
			removedNode = head;
			head = head->next;
		}
		//正常刪除結點
		else{
			removedNode = aheadOfRemove->next;
			aheadOfRemove->next = aheadOfRemove->next->next;
		}
		length--;
		delete aheadOfRemove;
		
	}

	//判斷鏈表是否爲空
	int isEmpty(){
		return head == 0;
	}
	//打印鏈表內容及相關參數
	void printSelf(){
		printf("SortedList: [");
		for (Node* now=head; now!=0; now=now->next)
		if (now->down == 0)
			printf("%d, ", now->value);
		else
		 	printf("%d( %d), ", now->value, now->down->value);
		printf("]\t\t%d\n", length);
		
	}
	private:
	Node* insertToHead(int value){
		if (head == 0){
			head = tail = new Node(value, NULL, NULL);
			//cout<<"Heading into Empty List"<<endl;
		}	
		else {
			head = new Node(value, head);
		}
		return head;
	}
	
};


//跳躍表類
class SkipList{
	public:
		int length;		//跳躍表最底層長度
		int layerNum;	//跳躍表最大層數
		SortedList* layers[100];	//跳躍表層數的引用
		//爲什麼這裏使用了數組?
		//因爲跳躍表的層數是log(n),100層能夠容納2^100個數據,使用數組佔用的空間可以忽略
	
	//構造函數
	SkipList(){
		length = 0;
		layerNum = 0;
		for (int i=0; i<100; i++)
			layers[i] = new SortedList();
		srand( (unsigned)time( NULL ) );
	}

	//向跳躍表插入值
	int insert(int value){
		//get the necessary layerNum by number of nodes

		//先獲得增加結點之後,該結點需要加入到哪些層
		int newLayerNum = getLayerNumRandomly(length+1);
		//更新最大層數量
		if (layerNum < newLayerNum){
			layerNum++;
		}

		//因爲先要從上向下查找到要將新結點插入到哪裏,並且需要將查找的路徑記錄下來
		//這樣才能在每一層都插入該結點
		//該數組用於記錄從上向下尋找的過程中,經過了哪些層的哪個結點(下標爲層數,數組內容爲經過的結點)
		Node* aheadOfInsert[100];
		//數組初始化爲NULL
		for (int i=0; i<100; i++)
			aheadOfInsert[i] = 0;
		
		//獲取每一層需要插入結點的位置
		getInsertIndex(value, aheadOfInsert);
		
		Node* InsertedNode[100] = {0};
		//cout<<"LayerNum: "<<layerNum<<endl;
		
		//遍歷每一層,插入結點
		for (int i=layerNum;i>=0;i--){
			//cout<<"Insert into layer "<<i<<" value "<<value<<endl;
			//if (i==1)cout<<"-=========================================="<<endl;
			//cout<<"aheadOfInsert value: "<<aheadOfInsert[i]->value<<endl;
			InsertedNode[i] = layers[i]->insertNode(value, aheadOfInsert[i]);
			//cout<<"asdasdasdasdasdas"<<endl;
		}
		
		//更新層與層之間的指針
		for (int i=layerNum; i>=1; i--){
			InsertedNode[i]->down = InsertedNode[i-1];
		}
		length++;
		//打印各個層
		cout<<"================================"<<endl;
		layers[0]->printSelf();
		layers[1]->printSelf();
		layers[2]->printSelf();
		layers[3]->printSelf();
		layers[4]->printSelf();
		cout<<"================================"<<endl;
	}

	//因爲先要從上向下查找到要將新結點插入到哪裏,並且需要將查找的路徑記錄下來
	//這樣才能在每一層都插入該結點
	//這個函用於查找該路徑,該路徑用要插入的結點位置的前一個結點表示,存入aheadOfInsert數組
	int getInsertIndex(int value, Node** aheadOfInsert){
		int i;
		Node* currentNode = 0;
		cout<<layerNum<<endl;

		//最高的層可能爲空,需要在鏈表頭插入結點,所以前一個結點爲空
		for (i=layerNum; i>=0 && (layers[i]->head==0 || layers[i]->head->value>value); i--){
			aheadOfInsert[i] = 0;
		}
		//如果所有層都爲空,直接返回
		if (i==-1)
			return 0;
		currentNode = layers[i]->head;
		
		//從上層到下層遍歷,尋找該路徑
		for (; i>=0; i--){
			Node* now;
		
			//該循環從一層的鏈表前方向後方遍歷,循環停止的條件有兩個:
			//當遇到鏈表尾:說明該值只可能在下一層找到
			//當遇到比當前值還小的結點,說明要找的值,在當前結點和下一和結點之間,轉向下一層尋找
			for (now=currentNode; now->next!=0 && now->next->value<value; now=now->next);
			aheadOfInsert[i] = now;
			currentNode = now->down;
		}
		
	}
	
	//因爲先要從上向下查找到要刪除哪個結點,並且需要將查找的路徑記錄下來
	//這樣才能在每一層都刪除該結點
	//這個函用於查找該路徑,該路徑用要刪除的結點位置的前一個結點表示,存入aheadOfRemove數組
	//然而,在刪除結點時,有如下三種情況:
		//1、該結點不存在這一層,不需要刪除,指針爲NULL
		//2、該結點是該層的第一個結點,該結點的前一個結點爲NULL,指針也爲NULL
		//3、找到了該結點,指針正常
	//所以,需要另一個數組表示該結點是否應該被刪除,這裏使用數組deleteOrNot
	int getRemoveIndex(int value, Node** aheadOfRemove, bool* deleteOrNot){
		int i;
		Node* currentNode = 0;

		//從上層向下層遍歷,如果該層爲空,或該層第一個值已經大於要刪除的值,說明這一層不需要刪除結點
		for (i=layerNum; i>=0 && (layers[i]->head==0 || layers[i]->head->value>value); i--){
			aheadOfRemove[i] = 0;
			deleteOrNot[i] = false;
		}
		//循環退出之後,i指向需要有結點有可能被刪除的第一個層
		cout<<i<<endl;
		if (i==-1)
			return 0;
		
		//遍歷可能需要刪除結點的層

		//如果要刪除的結點是底層鏈表的第一個元素,那麼向上找,把所有頭元素是該元素的鏈表首元素全部刪除
		if (layers[0]->head->value == value){
			i=0;
			while(layers[i]->head!=0 && layers[i]->head->value == value){
				deleteOrNot[i] = true;
				aheadOfRemove[i] = 0;
				i++;
			}
			return 0;
		}
		currentNode = layers[i]->head;
		
		//否則向下沿路徑查找,尋找應該刪除的結點
		for (; i>=0; i--){
			Node* now;
		
			for (now=currentNode; now->next!=0 && now->next->value<value; now=now->next);
			if (now->next!=0 && now->next->value == value){
				deleteOrNot[i] = true;
			}
			aheadOfRemove[i] = now;
			currentNode = now->down;
		}
	}

	//刪除某個元素的函數
	int remove(int value){
		//每一層需要被刪除的結點的前一個結點
		Node* aheadOfRemove[100] = {0};
		//該層的這個結點是否有效,或者說,如果aheadOfRemove【i】==null,是代表不需要刪除任何結點,還是刪除第一個結點
		bool removeOrNot[100] = {false};
		cout<<"Got removeIndex";
		
		//獲取每一層需要被刪除的結點的前一個結點
		getRemoveIndex(value, aheadOfRemove, removeOrNot);
		cout<<"Got removeIndex";
		//刪除結點
		for (int i=layerNum; i>=0; i--)
			if (removeOrNot[i] == true){
				layers[i]->deleteNode(aheadOfRemove[i]);
			}
		length--;

		//打印結果
		cout<<"================================"<<endl;
		layers[0]->printSelf();
		layers[1]->printSelf();
		layers[2]->printSelf();
		layers[3]->printSelf();
		layers[4]->printSelf();
		cout<<"================================"<<endl;
	}
	
	//在跳躍表中搜索某個元素
	Node* search(int value){
		int i;
		Node* currentNode = 0;
		
		//從上層向下層遍歷,如果該層爲空,或該層第一個值已經大於要查找的值,說明這一層不需要查找
		for (i=layerNum; i>=0 && (layers[i]->head==0 || layers[i]->head->value>value); i--);
		if (i==-1)
			return 0;
		cout<<"i = "<<i<<endl;	
		currentNode = layers[i]->head;
		
		Node* now;
		//從上層向下層遍歷查找
		for (; i>=0; i--){
			for (now=currentNode; now->next!=0 && now->next->value<=value; now=now->next);
			currentNode = now->down;
		}
		return now;
	}
	
	//隨機獲取某個結點應該被插入到0-k層
	int getLayerNumRandomly(int supposeLength){
		int k = 0;
		
		//隨機數
		int a = rand()%2;
		while(a==0){
			cout<<a<<" ";
			k++;
			a = rand()%2;
		}

		//根據二分的思想,查找n個結點,需要log(n)次
		//跳躍表裏也就需要log(n)層,所以這裏對隨機產生的層數有最大值限制
		int maxLayer = getMaxLayer(supposeLength);
		if (k > maxLayer)
			k = maxLayer;
			
		return k;
	}
	
	int getMaxLayer(int supposeLength){
		return ceil(log(supposeLength)/log(2));
	}
};

//測試函數

int main(){
	
	SkipList* list = new SkipList();
	list->insert(33);
	list->insert(1);
	list->insert(11);
	list->insert(21);
	list->insert(-1);
	list->insert(45);
	list->insert(37);
	
	Node* result = list->search(11);
	cout<<"Search result: "<<result->value<<endl;
	cout<<"next Search result: "<<result->next->value<<endl;
	
	list->remove(11);
	list->remove(-1);
	list->remove(45);
	/*
	for (int i=0; i<=100; i++){
		printf("%d: %d\n", i, list->getMaxLayer(i));
	}
	*/
}











        

        

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