資料結構--無頭單向非迴圈連結串列介面的實現
阿新 • • 發佈:2020-12-21
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"