1. 程式人生 > >C語言單鏈表

C語言單鏈表

  學過線性表中的順序表的都知道,順序表裡的資料在實體記憶體上是相鄰的,所以當我們在順序表中想要訪問下一個元素時可以直接去訪問,就像陣列一樣。但是單鏈表卻不同,單鏈表的資料儲存的位置是動態分配的,也就是說單鏈表的儲存在實體記憶體上不是相鄰的,所以我們就只能通過指標這種方式來把單鏈表串起來,通過指標來訪問下一個結點。

單鏈表結點

  單鏈表的結點裡有資料域和指標域,資料域是用來儲存資料的,指標域是用來儲存直接後繼結點的域。
typedef struct Node{
	int data;//資料域
	struct Node *next;//指標域
}LinkList,*Link;

建立單鏈表

  這裡我建立的是帶頭頭結點的單鏈表,頭結點裡並不儲存你輸入的資料,但可以儲存一些例如表長的資料,同時頭結點的next指標指向下一個首元結點也就是第一個開始儲存資料的結點,單鏈表結構如下圖:
  在我們建立頭結點的同時會用一個指標來指向頭結點也就是頭指標,這個頭指標對於整個連結串列來說是非常重要的,因為我們每次想要來遍歷連結串列中的資料時必須要先找到頭指標然後才能一個一個往下找到所有的資料,所以我們不能把單鏈表的頭指標丟了。還有連結串列的最後一個結點的指標要指向空,也可以指向頭結點,指向頭結點之後連結串列也就成了迴圈連結串列,當我們把最後一個結點的指標指向空時,我們就可以判空值來判斷是不是最後一個結點。 建立連結串列的程式碼:
//建立連結串列 
void CreasteList(Link H){//這裡H是一個結構體指標,我在宣告結構體時Link是帶*號的所以這裡Link就是Link*
	Link P,newNode;//P是用來指向頭結點的指標,因為我上面也說過了頭指標不能動,所以我們就建立一個頭指標的副本P,通過P來遍歷所有結點
	int i = 1,s;//s是我們輸入的資料,i是此時連結串列的長度
	P = H;//將P指向了頭結點,也就是建立了一個頭指標的副本
	printf("請輸入第%d個元素(-1結束):",i);
	scanf("%d",&s);
	while(s!=-1){//輸入-1時結束輸入
		newNode = (Link)malloc(sizeof(LinkList));//為新的結點分配空間
		newNode->data = s;//將輸入的s存入新結點newNode中
		P->next = newNode;//將此時的P的指標指向新建立的結點
		P = newNode;//這一點是將P從上一個結點指向新建立的結點,也就是指向此時連結串列的最後一個結點
		i++;//連結串列的長度增加1
		printf("請輸入第%d個元素(-1結束):",i);
		scanf("%d",&s);
	}
	ListLength = i;//連結串列的長度
	P->next = NULL; //最後結束輸入後P指向的是最後一個結點,將最後一個結點的next指向空
}

列印單鏈表

  列印單鏈表我們只需要讓頭指標的副本P不斷的指向下一個結點,遍歷了整個連結串列,結束條件就是尾結點的next為NULL。

//列印連結串列
void PrintList(Link H){
	Link P=H;//P指向頭結點
	while(P->next){//判斷最後一個結點後繼為NULL時結束
		P = P->next;//不斷的指向單鏈表下一個結點
		printf("%d ",P->data);	
	}
	printf("\n");
}

單鏈表的刪除操作

  要刪除一個單鏈表中的一個元素不需要像順序表那樣把刪除位置的後面的資料都前移一個位置,只需要改變一下指標的指向就可以了。


  所以相比順序表,單鏈表的刪除操作的時間複雜度要低很多。 程式碼如下:
//刪除連結串列
void DeleteList(Link H,int i){//H為連結串列的頭指標,i為你要刪除的位置
	Link P = H;
	Link deleP;//要刪除的指標
	int j = 0;//j是一個標記變數,表示此時是第幾個結點
	if(i<=0||i>ListLength) printf("你要刪除的位置不存在!\n");//當i的值小於0或者大於連結串列長度時,刪除的結點不存在
	else{
		while(P->next&&j<i-1){//j<i-1時,即當P指向需要刪除的第i個結點的前一個結點時結束迴圈
			P = P->next;//不斷的指向下一個結點
			j++;
		}
		deleP = P->next;//此時的P指向需要刪除的結點的前一個結點,deleP指向需要刪除的結點
		P->next = deleP->next;//將P的next指向deleP的next指向的結點,例如上圖將2的next指標指向4
		free(deleP);
		printf("刪除成功!\n");
		ListLength--;//刪除成功後連結串列長度減1
	}
}

單鏈表的插入操作

  同樣的單鏈表的插入操作要比順序表的時間複雜度低很多,單鏈表插入時不需要把所有元素後移,只需要改變指標的指向。
程式碼如下:
//插入連結串列 
void InsertList(Link H,int i){//i為插入的位置
	Link P = H;//P指向頭結點
	int j=0;
	if(i<=0||i>ListLength+1) printf("你插入的位置有錯誤!");//當插入位置小於=0和大於連結串列長度加1時,插入有誤,連結串列長度加1是因為可以在連結串列的尾部加結點
	else{
		Link cnew = (Link)malloc(sizeof(LinkList));//為需要插入的結點分配空間
		printf("請輸入你要插入的資料:\n");
		scanf("%d",&(cnew->data)); 
		while(P->next&&j<i-1){//同樣的j<i-1執行結束時P指向了需要插入的位置的前一個結點
			P = P->next;
			j++;
		}
		cnew->next = P->next;//將新結點的next指向P->next,即如上圖將P的next指向4
		P->next = cnew;//然後將P的next指向新結點
		printf("插入成功!");
		ListLength++;//插入成功後連結串列長度加1
	}
}

單鏈表查詢的實現

  查詢時只需將查詢的資料和連結串列的每一個數據進行配對,配對成功時返回此時的資料位置。
// 查詢元素
int FindList(Link H,int s){//s為查詢的資料
	Link P = H;
	int i=0;
	while(P->next&&P->data!=s){//結束的判定為P的next為空和資料配對成功時,當配對成功時結束,此時的P的data就是與s配對成功結點
		i++;
		P = P->next;
	}
	if(i>ListLength||P->data!=s) return 0;//如果i的值大於了連結串列的長度或者此時的P->data不是s,那麼就是連結串列中沒有和s配對成功的結點
	else return i;//配對成功返回此時的結點的位置
}

所有程式碼

#include<stdio.h>
#include<malloc.h>
int ListLength = 0;//連結串列長度 
typedef struct Node{
	int data;
	struct Node *next;
}LinkList,*Link;
void CreasteList(Link H);//建立連結串列
void InsertList(Link H,int i);//插入連結串列,i為插入位置
void DeleteList(Link H,int i);//刪除,i為刪除的位置
int FindList(Link H,int s);//查詢,i為查詢的資料,查詢連結串列中是否有這個資料 
void PrintList(Link H);//列印連結串列
int GetListLength(Link H);//獲取連結串列長度 
void SortInsert(Link H,int s);//有序插入 


int main(){
	Link H;
	H = (Link)malloc(sizeof(LinkList));//產生頭結點
	CreasteList(H);
	PrintList(H);
	SortList(H);
	PrintList(H);
	return 0;
} 
//建立連結串列 
void CreasteList(Link H){
	Link P,newNode;
	int i = 1,s;
	P = H;
	printf("請輸入第%d個元素(-1結束):",i);
	scanf("%d",&s);
	while(s!=-1){
		newNode = (Link)malloc(sizeof(LinkList));
		newNode->data = s;
		P->next = newNode;
		P = newNode;
		i++;
		printf("請輸入第%d個元素(-1結束):",i);
		scanf("%d",&s);
	}
	ListLength = i;
	P->next = NULL; 
}
//列印連結串列
void PrintList(Link H){
	Link P=H;
	while(P->next){
		P = P->next;
		printf("%d ",P->data);	
	}
	printf("\n");
}
//獲取連結串列長度 
int GetListLength(Link H){
	Link P = H;
	int length = 0;
	while(P->next){
		length++;
		P=P->next;
	}
	return length;
}
//刪除連結串列
void DeleteList(Link H,int i){
	Link P = H;
	Link deleP;
	int j = 0;
	if(i<=0||i>ListLength) printf("你要刪除的位置不存在!\n");
	else{
		while(P->next&&j<i-1){
			P = P->next;
			j++;
		}
		deleP = P->next;
		P->next = deleP->next;
		free(deleP);
		printf("刪除成功!\n");
		ListLength--;
	}
} 
//有序插入
void SortenInsert(Link H,int s){
	Link P = H;
	Link newNode = (Link)malloc(sizeof(LinkList));
	newNode->data = s;
	while(P->next&&P->next->data<s){
		P = P->next;
	}
	if(P->next==NULL){
		P->next = newNode;
		newNode->next = NULL;
	}else{
		newNode->next = P->next;
		P->next = newNode; 
	}
	ListLength++;
} 
//插入連結串列 
void InsertList(Link H,int i){
	Link P = H;
	int j=0;
	if(i<=0||i>ListLength+1) printf("你插入的位置有錯誤!");
	else{
		Link cnew = (Link)malloc(sizeof(LinkList));
		printf("請輸入你要插入的資料:\n");
		scanf("%d",&(cnew->data)); 
		while(P->next&&j<i-1){
			P = P->next;
			j++;
		}
		cnew->next = P->next;
		P->next = cnew;
		printf("插入成功!");
		ListLength++;
	}
}
// 查詢元素
int FindList(Link H,int s){
	Link P = H;
	int i=0;
	while(P->next&&P->data!=s){
		i++;
		P = P->next;
	}
	if(i>ListLength||P->data!=s) return 0;
	else return i;
}