用c語言實現資料結構——單連結串列

StudyCcYa發表於2020-11-08

單連結串列

用通俗的語言來說,單連結串列就像是頭和尾沒有連線起來的自行車鏈,每一個節點都是獨立的,卻又相互聯絡。特點:地址不連續,但是又相互有聯絡。

建立連結串列

1.頭插法:
(1)為了操作方便,一般情況下,連結串列的頭節點是不存放資料的。
(2)如果輸入1 2 3 4,那麼輸出是 4 3 2 1,是相反的。從這裡我們就有了頭插法的思路,首先讓頭節點(head)和第一個新的節點連線(temp1)。之後的新節點(temp2)要插入到第一個新節點(temp1)的前面。在圖中的表現如下:
第一步:讓頭指標和第一個temp連線起來:
在這裡插入圖片描述
第二步:第二個temp連線到第一個temp的前面:
在這裡插入圖片描述

第三步:頭節點和第二個temp連線(頭連temp):
在這裡插入圖片描述
最後一步就是頭節點和第一個temp斷開:
在這裡插入圖片描述
後續就以此類推了。
程式碼如下:

temp->next = head->next;
head->next = temp;

2.尾插法:
(1)頭節點依舊不存放資料
(2)如果輸入 1 2 3 4,那麼就輸出 1 2 3 4,思路:需要一個節點指標tail,可以理解為遊標,防止頭指標丟失的。一開始只有一個節點的時候,連結串列的頭和尾是一樣的。所以一開始會有tail=head。
第一步:頭和尾指向同一個位置:
在這裡插入圖片描述
第二步:尾和新節點temp連線:
在這裡插入圖片描述

第三步:新節點temp成為新的尾巴:
在這裡插入圖片描述

後續就以此類推了
程式碼如下:

tail->next = temp;/*先連線兩個節點*/
tail = tail->next;/*尾指標後移*/

當你能看懂建立連結串列的時候,後面的基本操作基本也都會了,要說的也就是刪除這裡了。

刪除對應位置節點

原理:找到要刪除的位置的節點的前一個節點,然後讓刪除位置前一個節點的next指標指向它的下下個節點,接著釋放掉刪除位置的節點。這就完成了刪除操作。

圖解:
在這裡插入圖片描述
假如我要刪除第2個節點,那麼我就要用一個指標遍歷到第1個節點:
在這裡插入圖片描述
這個時候節點1和節點3直接連線起來:
在這裡插入圖片描述
最後釋放掉節點2,這就相當於本來你和你女朋友拉著手呢,結果來了另一個比你帥比你有錢的男孩,拉走了你的女朋友,然後你被拋棄了哈哈哈。
在這裡插入圖片描述
程式碼如下:

/*
Function name :deleteNode
Description : 指定位置刪除節點
Parameter   : 
              @head : 單連結串列的頭指標
			  @i    : 要刪除的位置
return      : 返回1成功,返回其他失敗
*/
int deleteNode(Node *head, int i)
{
	Node *temp = head;/*防止頭指標丟失*/
	int num = getNum(head);/*獲取連結串列長度*/
	if (i<1 || i>num) return 0;/*刪除位置不合法*/

	for (int k = 0; k < i - 1; k++)/*temp指標移到要刪除的節點前*/
	{
		temp = temp->next;
	}
	Node *willDelete = temp->next;/*要刪除的節點*/
	temp->next = willDelete->next;/*隔著要刪除的節點連線*/
	free(willDelete);/*釋放要刪除的節點*/
	return 1; 
}

完整程式碼,註釋和執行結果

講在前面,如果你瞭解單連結串列的基本原理,那麼這個程式碼看起來會很輕鬆。

/*********************************************
@File name : singleChainList.c
@Author : StudyCcYa
@Version : 1.0
@Date : 2020-11-08
@Description : 用c語言實現資料結構——單連結串列
********************************************/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>


typedef int Type;

/*單連結串列節點的結構體*/
typedef struct Node
{
	Type data;/*單連結串列的資料*/
	struct Node *next;/*用於指向下一個節點的結構體指標*/
}Node;

Node *initNode();/*初始化單連結串列節點*/
Node *emptyNode();/*建立一個空的節點*/
Node *creatListHead();/*頭插法建立一個單連結串列*/
Node *creatListTail();/*尾插法建立一個單連結串列*/
int printList(Node *head);/*輸出單連結串列中的所有節點*/
int getNum(Node *head);/*統計節點個數*/
int insertElem(Node *head, int i,Type elem);/*指定位置增加(插入)資料*/
int deleteNode(Node *head, int i);/*指定位置刪除節點*/
Type getNode(Node *head, int i);/*查詢指定位置的資料*/
int changeNode(Node *head, int i,Type elem);/*修改指定位置的資料*/

/*
Function name : initNode
Description : 初始化單連結串列節點
Parameter : 
           無
return   : 返回一個單連結串列節點,否則失敗
*/
Node *initNode()
{
	Node *temp = (Node *)malloc(sizeof(Node));/*申請一個節點的空間*/
	assert(temp);/*斷言,如果申請空間失敗則報錯*/
	temp->data = 0;
	temp->next = NULL;
	return temp;
}

/*
Function name : emptyNode
Description : 建立一個空的節點
Parameter : 
            無
return    : 返回一個空的單連結串列節點,否則失敗
*/
Node *emptyNode()
{
	Node *temp = (Node *)malloc(sizeof(Node));/*申請一個節點的空間*/
	assert(temp);
	temp->next = NULL;
	return temp;
}

/*
Function name : creatList
Description : 頭插法建立一個單連結串列
Parameter :
            無
return   : 返回一個單連結串列的頭指標,其他失敗
*/
Node *creatListHead()
{
	int n;
	Type elem;
	Node *head = emptyNode();/*頭指標不存放資料*/
	printf("請輸入要建立連結串列節點的數量(頭插):\n");
	scanf("%d",&n);
	printf("請輸入資料:\n");
	for (int i = 0; i < n; i++)
	{
		Node *temp = initNode();
		scanf("%d",&temp->data);
		temp->next = head->next;/*新節點的next指標和頭節點的next的指標指向同一位置*/
		head->next = temp;/*連線*/
	}
	return head;
}

/*
Function name : creatListTail
Description : 尾插法建立單連結串列
Parameter :
@head : 單連結串列的頭指標
return     : 返回一個頭指標,返回其他失敗
*/
Node *creatListTail()
{
	Node *head = emptyNode();/*頭指標不存放資料*/
	Node *tail = head;/*一開始頭尾指向位置一樣*/
	int n;
	printf("請輸入要建立連結串列節點的數量(尾插):\n");
	scanf("%d", &n);
	printf("請輸入資料:\n");
	for (int i = 0; i < n; i++)
	{
		Node *temp = initNode();
		scanf("%d", &temp->data);
		tail->next = temp;/*先連線兩個節點*/
		tail = tail->next;/*尾指標後移*/
	}
	return head;
}

/*
Function name : printList
Description : 輸出單連結串列
Parameter : 
            @head : 單連結串列的頭指標
return    : 返回1成功,返回其他失敗
*/
int printList(Node *head)
{
	Node *temp = head->next;/*臨時連結串列指標,防止頭指標丟失*/
	for (; temp != NULL; temp = temp->next)/*迴圈遍歷連結串列*/
	{
		if (temp->next != NULL)/*沒有到最後一個節點*/
		{
			printf("%d->", temp->data);
		}
		if (temp->next == NULL)/*到最後一個節點*/
		{
			printf("%d\n",temp->data);
			return 1;/*輸出成功*/
		}
	}
	return 0;
}

/*
Function name : getNum
Description : 統計節點個數
Parameter :
           @head : 單連結串列的頭指標
return    : 返回單連結串列節點的個數,返回其他失敗
*/
int getNum(Node *head)
{
	int i = 0;/*用於計數*/
	Node *temp = head->next;/*第一個節點不算*/
	while (temp!=NULL)
	{
		i++;
		temp = temp->next;
	}
	return i;
}

/*
Function name :insertElem
Description : 指定位置增加(插入)資料
Parameter : 
           @head: 單連結串列的頭指標
		   @i   : 插入的位置
		   @elem: 插入的資料
return    : 返回1成功,其他失敗
*/
int insertElem(Node *head, int i,Type elem)
{
	Node *temp = head;
	int num = getNum(head);/*獲取單連結串列的長度*/
	if (i<1 || i>num+1) return 0;/*插入位置不合法*/
	//if (i == 1) /*如果在最前面插入*/
	//{
	//	Node *newNode = initNode();
	//	newNode->data = elem;//賦值
	//	newNode->next = temp->next;/*這裡相當於一個頭插*/
	//	temp->next = newNode;
	//	return 1;
	//}
	//if (i >= 1 && i <= num)/*如果在中間插入*/
	//{
		for (int k = 0; k < i-1; k++)/*temp指標移到要插入的節點前,這個時候就體現出頭節點不儲存資料的好處了*/
		{
			temp = temp->next;
		}
		/*下面的程式碼段就跟上面一樣了*/
		Node *newNode = initNode();
		newNode->data = elem;
		newNode->next = temp->next;
		temp->next = newNode;
		return 1;
	//}
	return 0; /*表示插入失敗咯*/
	/*插到最後的情況是跟中間一樣的。所以不再做處理*/
}

/*
Function name :deleteNode
Description : 指定位置刪除節點
Parameter   : 
              @head : 單連結串列的頭指標
			  @i    : 要刪除的位置
return      : 返回1成功,返回其他失敗
*/
int deleteNode(Node *head, int i)
{
	Node *temp = head;/*防止頭指標丟失*/
	int num = getNum(head);/*獲取連結串列長度*/
	if (i<1 || i>num) return 0;/*刪除位置不合法*/

	for (int k = 0; k < i - 1; k++)/*temp指標移到要刪除的節點前*/
	{
		temp = temp->next;
	}
	Node *willDelete = temp->next;/*要刪除的節點*/
	temp->next = willDelete->next;/*隔著要刪除的節點連線*/
	free(willDelete);/*釋放要刪除的節點*/
	return 1; 
}

/*
Function name : getNode
Description : 查詢指定位置的資料
Parameter : 
            @head : 單連結串列的頭指標
			@i    : 要查詢的位置
return    : 返回1成功,返回其他失敗
*/
Type getNode(Node *head, int i)
{
	Node *temp = head;/*防止頭指標丟失*/
	int num = getNum(head);/*獲取單連結串列長度*/
	if (i<1 || i>num) return 0;/*查詢位置不合法*/
	for (int k = 0; k < i; k++)/*temp指標移動到要查詢的節點*/
	{
		temp = temp->next;
	}
	return temp->data;/*將資料返回到函式*/
}

/*
Function name : changeNode
Description   : 修改指定位置的資料
Parameter     :
                @head : 單連結串列的頭指標
				@i    : 修改的位置
				@elem : 替換的資料
return        : 返回1成功,其他失敗
*/
int changeNode(Node *head, int i, Type elem)
{
	Node *temp = head;/*防止頭指標丟失*/
	int num = getNum(head);/*獲取單連結串列長度*/
	if (i<1 || i>num) return 0;/*修改位置不合法*/
	for (int k = 0; k < i; k++)/*temp指標移動到要修改的節點*/
	{
		temp = temp->next;
	}
	temp->data = elem;/*替換資料*/
	return 1;
}

int main()
{
	Node *head = creatListHead();/*頭插法*/
	printList(head);/*輸出頭插連結串列*/
	Node *head1 = creatListTail();/*尾插法*/
	printList(head1);/*輸出尾插連結串列*/

	printf("增:\n");
	insertElem(head, 1, 66);/*第1個位置插入66*/
	printList(head);

	insertElem(head, 3, 77);/*中間位置插入77*/
	printList(head);

	insertElem(head, 8, 99);/*最後位置插入99*/
	printList(head);
	printf("刪:\n");
	deleteNode(head, 1);/*刪除第一個節點*/
	printList(head);

	deleteNode(head, 3);/*刪除中間節點*/
	printList(head);

	deleteNode(head, 6);/*刪除最後節點*/
	printList(head);

	printf("查:\n");
	printf("1:%d\n", getNode(head, 1));/*查詢第1個節點的資料*/
	printf("2:%d\n", getNode(head, 2));/*查詢中間節點的資料*/
	printf("5:%d\n", getNode(head, 5));/*查詢最後節點的資料*/

	printf("改:\n");
	changeNode(head, 1, 22);/*第1個節點資料改成22*/
	printList(head);

	changeNode(head, 3, 33);/*中間節點資料改成33*/
	printList(head);

	changeNode(head, 5, 44);/*最後節點資料改成44*/
	printList(head);
	return 0;
}

在這裡插入圖片描述
執行成功。

如有不足和建議,歡迎指正和討論。後續會將所有的資料結構用c語言和java語言實現。

相關文章