本文將使用 C 語言來實現一個單鏈表,並實現遍歷、查找、插入、刪除等操做。node
1、建立一個單鏈表算法
首先,定義一個存放結點相關信息的結構體,結構體有兩個元素,分別是鍵值和一個指向下一節點的指針。數據結構
/* 用於存放結點信息的結構體 */ struct node { int key; struct node *next; }; typedef struct node Node; typedef struct node *PtrToNode;
想要建立一個單鏈表,能夠先建立一個表頭結點(啞結點),而後在表頭結點後不斷插入新的結點便可,須要注意的是,每新建一個結點都要爲該結點分配一段內存空間。spa
/* 建立一個鏈表 */ PtrToNode CreateList(int listLen) { int i, keyValue; /* 建立一個表頭結點,併爲其分配內存空間 */ PtrToNode headPtr = (PtrToNode)malloc(sizeof(Node)); if (headPtr == NULL) { perror("malloc failed!\n"); exit(EXIT_FAILURE); } PtrToNode tailNode = headPtr; // 建立一個表尾結點,並將表頭結點賦給尾結點 tailNode->next = NULL; for (i = 0; i < listLen; i++) { /* 建立一個新結點,併爲其分配內存空間 */ PtrToNode newNode = (PtrToNode)malloc(sizeof(Node)); if (newNode == NULL) { perror("malloc failed!\n"); exit(EXIT_FAILURE); } printf("請輸入第 %d 個結點的鍵值:", i + 1); scanf_s("%d",&keyValue); // 將 newNode 插入鏈表尾部 newNode->key = keyValue; // 賦鍵值 newNode->next = NULL; // next 指針指向 NULL tailNode->next = newNode; // 這裏的 tailNode 存放的是上一次循環中建立的 newNode,也就是如今新建結點的前驅結點 // 故而這裏是將前驅結點的 next 指針指向當前結點 tailNode = newNode; // 將當前結點賦給 tailNode(實際上,tailNode 就起着一個臨時結點的做用) } return headPtr; }
2、遍歷一個單鏈表指針
/* 遍歷鏈表 */ void TraverseList(PtrToNode List) { PtrToNode ptr = List->next; if (ptr == NULL) { printf("鏈表爲空\n"); } while (ptr != NULL) { printf("%d ", ptr->key); ptr = ptr->next; } }
這段代碼根據鏈表表尾結點的 next 指針指向 NULL 來遍歷整個鏈表。code
3、查找一個元素blog
/* 查找一個元素 */ PtrToNode FindElement(PtrToNode List,int val) { PtrToNode ptr = List->next; if (ptr == NULL) { printf("鏈表爲空\n"); return NULL; } while (ptr != NULL && ptr->key != val) { ptr = ptr->next; } if (ptr != NULL) { printf("找到 %d 了\n", val); } else { printf("沒有找到 %d\n", val); } return ptr; }
這段代碼查找元素 val 是否在鏈表中,若是在,則打印元素已找到的信息,並返回該元素在鏈表中所在的結點;若是不在鏈表中,則打印沒找到的信息,並返回一個空指針。內存
4、插入一個元素it
/* 插入一個元素 */ void InsertElement(PtrToNode List,PtrToNode Position,int val) { PtrToNode tmpNode = (PtrToNode)malloc(sizeof(Node)); if (tmpNode == NULL) { perror("malloc failed!\n"); exit(EXIT_FAILURE); } tmpNode->key = val; tmpNode->next = Position->next; Position->next = tmpNode; }
這段代碼將元素 val 插入到鏈表中指定結點的後面。 io
5、刪除操做
5.1 刪除整個鏈表
/* 刪除整個鏈表 */ void DeleteList(PtrToNode List) { PtrToNode position, tmpNode; position = List->next; List->next = NULL; while (position != NULL) { tmpNode = position->next; // 先將當前結點的 next 指針賦給臨時結點保存 free(position); // 而後釋放當前結點 position = tmpNode; // 再將以保存的 next 指針賦給 position,即爲下一個要刪除的結點 } }
刪除整個鏈表時,須要注意一點,要提早將要刪除結點的 next 指針保存下來,再釋放該結點。而不能在釋放了一個結點後再去利用已釋放結點的 next 指針去釋放下一個結點,由於此時上一個結點已經被釋放了,故而找不到 next 指針。此外,因爲在建立鏈表時,每插入一個新的結點都會用 malloc 來給結點分配一塊內存,故而在刪除鏈表時,每釋放一個結點也應該使用 free 來釋放一次內存。
5.2 刪除一個元素
/* 刪除一個元素 */ void DeleteElement(PtrToNode List,int val) { PtrToNode tmpNode; PtrToNode prev_position = FindPrevNode(List, val); if (prev_position->next == NULL) { printf("要刪除的元素不存在!\n"); } else { tmpNode = prev_position->next; prev_position->next = tmpNode->next; free(tmpNode); } } /* 獲取元素的前驅結點 */ PtrToNode FindPrevNode(PtrToNode List, int val) { PtrToNode prev_position = List; while (prev_position->next != NULL && prev_position->next->key != val) { prev_position = prev_position->next; } return prev_position; }
刪除一個元素時,須要先找到該元素的前驅結點。
參考資料:
《算法導論 第三版》
《數據結構與算法分析——C語言描述》