1. 程式人生 > 其它 >C語言實現連結串列【一】(無頭單向非迴圈連結串列)

C語言實現連結串列【一】(無頭單向非迴圈連結串列)

無頭單向非迴圈連結串列

看到這個標題,是不是有小夥伴已經懵了呢?

只學過連結串列,怎麼還有個無頭和有頭呢?怎麼還有個迴圈和非迴圈呢?怎麼還有個單向和雙向呢?一連串的疑問。。。

其實這些都是連結串列的表示形式,只不過大部分人只接觸了最簡單的單鏈表,也就是我們這裡提到的無頭單向非迴圈連結串列,最簡單的連結串列結構表示形式

話不多說,直接上圖更直觀一些

  • 單鏈表結構定義:

熟悉了單鏈表的結構,下面來考慮如何在連結串列中實現資料儲存呢?

連結串列是由多個節點按照一定的邏輯順序連線形成的,每一個節點都由兩部分組成,一部分儲存資料,一部分存放下一節點的地址

程式碼實現結構如下:

typedef int LDatatype;

//單個節點資料結構
typedef struct listNode {
	LDatatype data;
	struct listNode* next;
}listNode;

此處我們需要區分單鏈表和雙鏈表:

單鏈表只能在前驅節點中存放後繼節點的地址,而雙鏈表不僅能夠在前驅節點存放後繼節點地址,還可以在後繼節點中存放前驅節點的地址

區分不帶頭單鏈表和帶頭帶頭單鏈表

帶不帶頭不是說看有沒有head,而是看head節點是否存放資料

對此,即便是無頭的單鏈表在構建連結串列結構的過程中,依然在頭結點存放第一個節點的地址,但頭結點僅僅放地址,而不存放資料

typedef struct linklist {
	//儲存第一個節點的地址
	listNode* head;
}linklist;
  • 單鏈表介面宣告:

單鏈表主要也是實現資料管理儲存功能,即增刪查改的操作,包括在頭節點插入,尾結點插入,頭結點刪除,尾結點刪除,指定節點後刪除插入操作等

void linklistInit(linklist* lst);

void linklistPushBack(linklist* lst,LDatatype val);

listNode* createNode(LDatatype val);

void linklistPopBack(linklist* lst);

void linklistPushFront(linklist* lst,LDatatype val);

void linklistPopFront(linklist* lst);

void linkInsertAfter(listNode* node, LDatatype val);

void linkPopAfter(listNode* node);

listNode* linkFind(linklist* lst, LDatatype val);

void linkDestory(linklist* lst);

void linkPrint(linklist* lst);
  • 單鏈表介面實現

  • 連結串列初始化:

(1)初始化連結串列結構:

由於連結串列結構中有頭結點,初始化只需要將頭結點存放地址置為空

void linklistInit(linklist* lst)
{
	if (lst == NULL)
		return;
	lst->head = NULL;
}

(2)建立節點

當執行插入操作的過程中,根據節點結構需要先構造出一個可以存放資料的節點

listNode* createNode(LDatatype val)
{
	listNode* node = (listNode*)malloc(sizeof(listNode));
	if (node != NULL)
	{
		node->data = val;
		node->next = NULL;
	}
	return node;
}

注意到在建立節點中,將該節點存放下一節點地址給NULL,如果尾插,將不需在尾部節點置NULL,其餘位置插入,更新地址即可

  • 單鏈表尾插

void linklistPushBack(linklist* lst, LDatatype val)
{
	//空 建立節點 賦值+next置空
	if (lst == NULL || lst->head == NULL)
	{
		lst->head = createNode(val);
	}
	//非空  遍歷找到尾結點  在其後插入
	else
	{
		listNode* tail=lst->head;
		while (tail->next != NULL)
		{
			tail = tail->next;
		}
		tail->next = createNode(val);
	}
}
  • 單鏈表尾刪

只有一個頭結點情況:

遍歷找最後一個節點:

程式碼如下:

void linklistPopBack(linklist* lst)
{
	if (lst == NULL || lst->head == NULL)
		return;
	//只有一個頭結點  
	if (lst->head->next == NULL)
	{
		lst->head = NULL;
	}
	//否則,遍歷找到最後一個節點
	listNode* prev = NULL;
	listNode* tail = lst->head;
	//兩個指標同時後移查詢
	while (tail->next!=NULL)
	{
		prev = tail;
		tail = tail->next;
	}
	free(tail);
	prev->next = NULL;
}
  • 單鏈表頭插

void linklistPushFront(linklist* lst,LDatatype val)
{
	if (lst == NULL || lst->head == NULL)
		return;
	listNode* node = createNode(val);
	node->next = lst->head;
	lst->head = node;
}
  • 單鏈表頭刪

void linklistPopFront(linklist* lst)
{
	if (lst == NULL||lst->head==NULL)
		return;
	listNode* next= lst->head->next;
	free(lst->head);
	lst->head = next;
}
  • 單鏈表指定節點後插入

void linkInsertAfter(listNode* node, LDatatype val)
{
	if (node == NULL)
		return;
	listNode* newnode = createNode(val);
	listNode* next = node->next;
	node->next = newnode;
	newnode->next = next;
}
  • 單鏈表指定節點後刪除

void linkPopAfter(listNode* node)
{
	if (node == NULL)
		return;
	//listNode* newnode = createNode(val);
	listNode* next = node->next;
	listNode* newnext = next->next;
	free(next);
	node->next = newnext;
}
  • 單鏈表遍歷
listNode* linkFind(linklist* lst, LDatatype val)
{
	if (lst == NULL || lst->head == NULL)
	{
		return NULL;
	}
	listNode* cur = lst->head;
	while (cur)
	{
		if (cur->data == val)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}
  • 單鏈表銷燬(刪除所有節點,但需要從頭開始刪除,並儲存後續節點)

void linkDestory(linklist* lst)
{
	if (lst == NULL || lst->head == NULL)
	{
		return;
	}
	listNode* cur = lst->head;
	//listNode* next = cur->next;
	while (cur)
	{
		listNode* next = cur->next;
		free(cur);
		cur = next;
		/*lst->head = next;
		cur = lst->head;
		next = next->next;*/
	}
	lst->head = NULL;
}
  • 單鏈表某一指定節點刪除

listNode* linkErase(linklist* lst,listNode* node)
{
	if (node == NULL)
		return;
	//listNode* newnode = createNode(val);
	listNode* tmp = (listNode*)malloc(sizeof(listNode));
	tmp->next = lst->head;
	listNode* prev = tmp;
	listNode* tail = lst->head;
	//兩個指標同時後移查詢
	while (tail)
	{
		if (tail == node)
		{
			prev->next = tail->next;
			tail = tail->next;
		}
		else
		{
			prev = prev->next;
			tail = tail->next;
		}
	}
	lst->head = tmp->next;
	free(tmp);
	return lst->head;
}

正所謂千言萬語的解釋,不如一張圖解釋的清楚,不理解的小夥伴可以根據圖示來理解連結串列的指向變化,

今天分享就到這,下一次分享帶頭雙向迴圈連結串列的增刪改查實現!