1. 程式人生 > 其它 >資料結構--無頭單向非迴圈連結串列介面的實現

資料結構--無頭單向非迴圈連結串列介面的實現

技術標籤:資料結構資料結構

1 無頭單向非迴圈連結串列結構

無頭單向非迴圈連結串列的示意圖如下:
在這裡插入圖片描述
它的定義如下:

//無頭單向非迴圈連結串列的定義
typedef int LDataType;
typedef struct ListNode {
LDataType data;
struct ListNode* next;
}ListNode;


typedef struct List {
ListNode* head;
}List;

2 無頭單向非迴圈連結串列介面的實現

2.1 標頭檔案及連結串列的定義

#include<stdio.h>
#include<stdlib.h>
typedef int LDataType; typedef struct ListNode { LDataType data; //儲存的資料 struct ListNode* next; //指向下一個節點 }ListNode; typedef struct List { ListNode* head; //指向連結串列節點的指標 }List;

2.2 連結串列的初始化

//連結串列的初始化
void listInit(list* lst) {
	//連結串列的初始化就是使head的指向為空
	if (lst == NULL)
		return;       //判斷引數是否有效
lst->head = NULL; }

2.3 建立一個新節點

//建立一個新節點
listNode* creatNode(LDataType val) {
	//建立一個連結串列節點,使節點的資料值為val,next指向空
	//返回建立節點的地址
	listNode* node = (listNode*)malloc(sizeof(listNode));
	//申請一個大小為listNode型別的空間,並返回該空間的地址
	node->data = val;
	node->next = NULL;
	return node;
}

2.4 尾插一個數據

//尾插一個數據
void ListPushBack
(list* lst, LDataType val) { /*1、先判斷連結串列是否為空連結串列,空連結串列直接進行尾插,非空則進行如下操作 2、得到連結串列的最後一個節點,地址儲存在end中 * 3、建立一個新的節點,地址儲存在node中 * 4、進行尾插 */ if (lst == NULL) return; //1、先判斷連結串列是否為空連結串列 if (lst->head == NULL) { lst->head = creatNode(val); return; } //2、得到最後的節點end listNode * end; end = lst->head; while (end->next != NULL) { end = end->next; } //3、建立一個新的節點 listNode* node = creatNode(val); //4、進行尾插 end->next = node; }

2.5 尾刪一個數據

//尾刪一個數據
void ListPopBack(list* lst) {
	/*
	* 1、首先判斷連結串列是否為空連結串列,若為空,則直接返回,若不為空,進行如下操作
	* 2、找到最後一個節點,記錄在end中,並記錄上一個節點的位置,記錄在perv中
	* 3、釋放最後一個節點,使prev->next=NULL
	*/
	//1、若連結串列為空,則直接返回
	if (lst == NULL || lst->head == NULL)
		return;
	//2、找到最後一個節點並記錄前一個節點
	listNode* prev, * end;
	prev = NULL;
	end = lst->head;
	while (end->next != NULL) {
		prev = end;
		end = end->next;
	}
	//prev可能為NULL,也可能不為NULL

	//3、釋放最後一個節點
	free(end);
	if (prev == NULL) {
		lst->head = NULL;
	}else
		prev->next = NULL;
}

2.6 頭插一個數據

//頭插一個數據
void listPushFront(list* lst, LDataType val) {
	/*1、判斷是否為空連結串列,空連結串列直接插入,非空則進行如下操作
	* 2、建立一個新的節點node
	* 3、node->next = lst->head , lst->head = node ;
	* 4、可以觀察出,空連結串列和非空連結串列的插入過程相同,可以進行一個合併
	*/
	if (lst == NULL)
		return;
	listNode* node = creatNode(val);
	node->next = lst->head;
	lst->head = node;     
	//注意:上面兩條語句的順序不能發生變化,否則會使連結串列後面的語句丟失
	/*若不考慮兩者的順序,也可以寫成
	* listNode* next = lst->head ;
	* lst->head = node ;
	* node->next = next ;
	*/
}

2.7 頭刪一個數據

void listPopFront(list* lst) {
	/*1、首先判斷連結串列是否為空,空連結串列直接返回
	* 2、記錄第二個節點的位置next和第一個節點的位置node
	* 3、釋放node
	* 4、lst->head = next 
	*/
	//1、空連結串列直接返回
	if (lst == NULL || lst->head == NULL) {
		return;
	}
	//2、記錄節點位置
	listNode* next, * node;
	node = lst->head;
	next = node->next;
	//3、刪除節點
	free(node);
	lst->head = next;

}

2.8 在給定節點後插入一個數據

//在給定節點之後刪除一個數據
void listEraseAfter(listNode* cur) {
	/*1、記錄下一個節點的位置next和下下個節點的位置nextnext
	* 2、釋放next;cur->next = nextnext
	*/
	listNode* next = cur->next;
	if (next == NULL)
		return;
	listNode* nextnext = next->next;
	free(next);
	cur->next = nextnext;
}

2.9 在給定節點之後刪除一個數據

//在給定節點之後刪除一個數據
void listEraseAfter(listNode* cur) {
	/*1、記錄下一個節點的位置next和下下個節點的位置nextnext
	* 2、釋放next;cur->next = nextnext
	*/
	listNode* next = cur->next;
	listNode* nextnext = next->next;
	free(next);
	cur->next = nextnext;
}

2.10 查詢某個節點

listNode* listFind(list* lst, LDataType val) {
	/*1、若為空連結串列,則直接返回
	* 2、遍歷整個連結串列,查詢data==val的節點
	*/
	if (lst == NULL || lst->head == NULL)
		return;
	listNode* cur = lst->head;
	while (cur) {
		if (cur->data == val) {
			return cur;
		}
		cur = cur->next;
	}
	return NULL;
}

2.11 求連結串列的長度

//求連結串列的長度
int listSize(list* lst) {
	/*1、若為空連結串列,則直接返回0
	* 2、遍歷整個連結串列,計算連結串列的長度
	*/
	if (lst == NULL || lst->head == NULL)
		return;
	listNode* cur = lst->head;
	int len = 0;
	while (cur) {
		len++;
		cur = cur->next;
	}
	return len;
}

2.12 列印連結串列

void printList(list* lst) {
	//遍歷整個連結串列依次進行列印
	if (lst == NULL)
		return;
	listNode* cur = lst->head;
	while (cur) {
		printf("%d ", cur->data);
		cur = cur->next;
	}
	printf("\n");
}

2.13 查詢第n個位置的節點

//查詢某個節點
listNode* listFindByIdx(list* lst, int n) {
	/*1、若為空連結串列,則直接返回
	* 2、遍歷整個連結串列,查詢第n個節點
	*/
	if (lst == NULL || lst->head == NULL)
		return;
	listNode* cur = lst->head;
	int len = 0;
	while (len < n && cur != NULL) {
		cur = cur->next;
		len++;
	}
	if (len >= n)
		return cur;
	if (cur == NULL)
		return NULL;
}

2.13 銷燬一個連結串列

//銷燬一個連結串列
void listDestroy(list* lst) {
	/*1、若為空連結串列則直接返回
	* 2、進行頭刪操作,直至連結串列清空
	*/
	if (lst == NULL || lst->head == NULL)
		return;
	listNode* cur = lst->head;
	while (cur) {
		lst->head = cur->next;
		free(cur);
		cur = lst->head;
	}
}

注意:
1、進行操作前,一般先考慮連結串列為空的情況,在考慮連結串列非空的情況;
2、若程式無法識別list型別,則寫全型別名:struct list;

3 驗證連結串列的介面

int main() {
	list lst;
	listInit(&lst);//初始化連結串列
	listPushBack(&lst, 1);//尾插1
	//連結串列為1
	printList(&lst); //列印連結串列
	listPushBack(&lst, 2);//尾插2
	//連結串列為1->2
	printList(&lst); //列印連結串列
	listPushFront(&lst, 0);//頭插0
	//連結串列為0->1->2
	printList(&lst); //列印連結串列
	
	listNode* cur = listFind(&lst ,0);//查詢值為0的節點
	listInsertAfter(cur, 11);//在值為0的節點的後方插入11
	//連結串列為0->11->1->2
	printList(&lst); //列印連結串列
	cur = listFindByIdx(&lst, listSize(&lst) - 1);
	listInsertAfter(cur, 13);//尾插13
	//連結串列為0->11->1->2->13
	printList(&lst); //列印連結串列

	listPopBack(&lst);//尾刪
	//連結串列為0->11->1->2
	printList(&lst); //列印連結串列
	listPopFront(&lst);//頭刪
	//連結串列為11->1->2
	printList(&lst); //列印連結串列
	cur = listFindByIdx(&lst, 0);
	listEraseAfter(cur);//刪除位置0之後的節點
	//連結串列為11->2
	printList(&lst); //列印連結串列
	cur = listFindByIdx(&lst, listSize(&lst) - 2);
	listEraseAfter( cur);//尾刪
	//連結串列為11
	printList(&lst); //列印連結串列
	listDestroy(&lst);//銷燬連結串列
	printList(&lst); //列印連結串列
	system("pause");
	return 0;
}

執行結果如下圖:
在這裡插入圖片描述

  • 一個小技巧
    可以將所有的全域性變數及函式宣告均定義在一個頭檔案中,這樣,就不用考慮.c檔案中函式的先後順序了。
    這裡標頭檔案內容如下:
typedef int LDataType;
typedef struct listNode {
	LDataType data;         //儲存的資料
	struct ListNode* next;   //指向下一個節點
}listNode;

typedef struct list {
	listNode* head;    //指向連結串列節點的指標
}list;

void listInit(list* lst);
listNode* creatNode(LDataType val);
void listPushBack(list* lst, LDataType val);
void listPopBack(list* lst);
void listPushFront(list* lst, LDataType val);
void listPopFront(list* lst);
void listInsertAfter(listNode* cur, LDataType val);
void listEraseAfter(listNode* cur);
void printList(list* lst);
listNode* listFind(list* lst, LDataType val);
listNode* listFindByIdx(list* lst, int n);
int listSize(list* lst);
void listDestroy(list* lst);

.c檔案中所要宣告的標頭檔案如下:

#include<stdio.h>
#include<stdlib.h>
#include"list.h"