1. 程式人生 > >靜態連結串列相關演算法學習

靜態連結串列相關演算法學習

大話資料結構學習筆記—靜態連結串列學習

c語言真是好東西,它具有指標能力,使得它可以非常容易地操作記憶體中的地址和資料,這比其他高階語言更加靈活方便。

後來的面向物件的語言,如java、C#等,雖然不使用指標,但是因為啟用了物件引用機制,從某種角度也間接實現了指標的
某些作用。但是對一些Basic、Fortran等早期的程式設計高階語言,由於沒有指標,連結串列結構按照前面我們的講法,它就沒法實現了。
有人想出來用陣列來代替指標。他們是怎麼做到的呢?
首先我們讓陣列的元素都是由2個數據域組成,data和cur。也就說,陣列的每個下標都對應一個data和一個cur。資料域,用來
存放資料元素,也就是我們要處理的資料;而遊標相當於單鏈表中的next指標,存放該元素的後繼在陣列中的下標。
我們把這種用陣列描述的連結串列叫做靜態連結串列

為了方便插入資料,我們通常會把資料建立的大一些,以便有一些空閒空間可以便於插入時不溢位。

靜態連結串列將陣列第一個元素用來指向備用連結串列(連結串列中沒用到的空間)中的第一元素,最後一個元素用來指向第一個存資料的元素相當於頭結點的作用。

資料結構

1、        用結構體模擬陣列成員

a)    typedef struct Element{DataTypedata,int cur}

2、        用結構體陣列模擬靜態連結串列相關演算法,長度確定後不可變 。

a)    typedef Element StaticLinkStatic[MAXSIZE] ;

初始化

陣列第一個元素指向第一個可用元素也就是第二個元素下標[1]。

陣列最後一個元素,類似頭指標作用,指向第一個存放資料元素的下標,空連結串列 cor 為0,其他空閒元素可初始化依次指向其後面一個元素。

插入操作

動態連結串列中分配空間使用malloc函式,但是靜態連結串列中,實質上我們定義連結串列時,空間大小已經確定了,那麼我們要插入資料同樣也要一個空間,只不過空間已經在連結串列中分配了只不過是空閒的還沒拿出來用了,我們需要一個類似的函式將空閒位置拿來存入我們需要的資料,這裡涉及到幾個要操作的地方

1)   取出一個備用元素後,那麼第一個元素指向備用連結串列要改變指向,指向取出元素的後繼元素。其實就是完成分配空間操作。

2)   插入元素時,要找到插入元素位置前一個元素 i-1,需要改變他的cur 指向改為插入元素的 下標,而插入元素 的cur指向 要改為 i-1指向元素的下標。

刪除操作

刪除元素空間,因為是靜態連結串列空間已經固定,不能真正的在記憶體將其釋放而是將其放到靜態連結串列的備用連結串列中,可以繼續供連結串列所使用。同樣需要類似free函式來釋放元素,操作和插入相反。

1)        將刪除元素的 前置結點 cur 指向 刪除元素的後置結點的下標

2)        釋放刪除元素所佔位置,將其置為備用鏈中的第一個可用元素

其他操作

獲取連結串列元素個數

最後一個元素cur為0 根據這個條件計數

列印連結串列元素

下面看下實現程式碼

#define MAXSIZE 500
#define OK 1
#define ERROR 0
#include <stdio.h>

typedef int Status;
typedef char DataType;
typedef struct Element Element;
typedef Element StaticLinkList[MAXSIZE];//結構體陣列
struct Element {
	DataType data;//資料
	int cur;//遊標,後繼結點的下標
};
//初始化靜態連結串列
//靜態連結串列將陣列第一個元素用來指向備用連結串列中的第一元素,最後一個元素用來指向第一個存資料的元素 相當於頭結點的作用,下標0和num-1 作為對外展示的連結串列元素。
Status initSLL(StaticLinkList sll) {
	int i = 0;
	//由於是空連結串列,list[0].cur 指向下標為2到MAXSIZE-2元素備用連結串列,這樣剛好一個指向一個
	//[0].cur為1 指向下標為1的元素,[1].cur位2指向下標為2的元素,依次類推
	//注意[MAXSIZE-2]為最後一個可用元素,指向[MAXSIZE-1]元素,做特殊處理,大話資料結構中 沒有做處理。
	for (i = 0; i < MAXSIZE; i++) {
		sll[i].cur = i + 1;
	}
	sll[MAXSIZE - 2].cur = 0;
	sll[MAXSIZE - 1].cur = 0;//空連結串列,它的cur是儲存元的第一個位置的下標。
	return OK;
}
//建立一個可以使用的下標元素,將備用鏈中的第一個元素下標返回,讓靜態連結串列第一個元素重新指向下一個備用元素。
int mollocEle(StaticLinkList sll) {
	int i = (sll)[0].cur;
	if (i) {//有可用的備用元素
		(sll)[0].cur = (sll)[i].cur;
	}
	return i;
}
//釋放第k個元素的讓其成為備用連結串列中的元素,讓靜態連結串列第一個元素(下標為0)的元素指向它,它指向原來備用鏈第一個元素 
void freeEle(StaticLinkList sll,int p) {
	sll[p].cur = sll[0].cur;
	sll[0].cur = sll[p].cur;
	return;
}

//列印靜態連結串列內容
void printStaticLinkList(StaticLinkList sll)
{
	int begin = sll[MAXSIZE - 1].cur;//找到起始元素
	Element e;
	//begin為0 要麼為空連結串列 要麼連結串列資料遍歷完畢
	while(begin) {
		e = sll[begin];
		printf("%c ", e.data);
		begin = e.cur;
	}
	return;
}
int lengthSSL(StaticLinkList sll) {
	int begin = sll[MAXSIZE - 1].cur;
	int i = 0;
	//begin 為0 要麼為空連結串列 要麼連結串列遍歷完畢
	while (begin)
	{
		begin = sll[begin].cur;
		i++;
	}
	return i;
}
//插入位置i,找到i-1元素,將i元素的cur指向i-1的後繼元素,將i-1元素指向插入元素即可
//i不是陣列下標而是連結串列中的第i個元素,cur儲存的才是下標
Status insertSLL(StaticLinkList sll, int i, DataType data) {
	if (i<1 || i>lengthSSL(sll)+1) {//可以插在元素末尾!比如說有5個元素可以插在第6位置,但不能插入到第7個以及後面
		return ERROR;
	}
	int j = mollocEle(sll);
	if (!j) {//分配空間失敗
		return ERROR;
	}
	int begin = MAXSIZE - 1;//第一次進入迴圈,找到第一個資料元素
	for (size_t k = 1; k <= i-1; k++)
	{
		begin = sll[begin].cur;
	}
	//迴圈結束 begin為i-1個元素的下標
	sll[j].cur = sll[begin].cur;
	sll[j].data = data;
	sll[begin].cur = j;

	return OK;
}
//刪除位置i,找到i-1個元素,將第i個元素的cur指向i-1的後繼元素,將i-1元素指向插入元素即可
Status delSLLEle(StaticLinkList sll, int i) {
	if (i<1 || i>lengthSSL(sll)) {
		return ERROR;
	}
	int begin = MAXSIZE - 1;//第一次進入迴圈,找到第一個資料元素
	for (size_t k = 1; k <= i - 1; k++)
	{
		begin = sll[begin].cur;
	}
	//迴圈結束 begin為i-1個元素的下標
	int curE = sll[begin].cur;//刪除元素的下標
	sll[begin].cur = sll[curE].cur;//i-1個元素指向 i+1個元素
	freeEle(sll,curE);//釋放i元素,放回備用連結串列
	return OK;
}
int main() {
	StaticLinkList L;
	Status i;
	i = initSLL(L);
	printf("初始化L後:L.length=%d\n", lengthSSL(L));
	/*
	注意第一個元素必須插入到第一位置!
	因為空表裡面沒有元素,插入位置不在範圍內同樣不能插入
	插入範圍 1 ~ 現有元素個數+1 都可以
	*/
	i = insertSLL(L, 1, 'A');
	i = insertSLL(L, 2, 'B');
	i = insertSLL(L, 3, 'C');
	i = insertSLL(L, 4, 'D');
	i = insertSLL(L, 5, 'E');

	printf("\n在L的表尾依次插入ABCDE後:\nL.data=");
	printStaticLinkList(L);

	i = insertSLL(L, 3, 'Y');
	printf("\n在L的“B”與“C”之間插入“Y”後:\nL.data=");
	printStaticLinkList(L);

	i = delSLLEle(L, 2);
	printf("\n在L的刪除“B”後:\nL.data=");
	printStaticLinkList(L);

	printf("\n");
	
	return 0;
}

驗證結果截圖: