要點
在順序表的算法文章中,我們討論了線性表的順序存儲結構——順序表。
順序表是用一組地址連續的存儲單元來保存數據的,所以它具有隨機存取的特點。即查找快速,但是做插入或刪除動作是,需要移動大量元素,效率較低。
鏈表
鏈表是線性表的鏈式存儲結構,它相比於順序表,在插入和刪除元素時,效率要高很多。
鏈表,是用一組任意的存儲單元存儲線性表的數據元素(這組存儲單元可以是連續的,也可以是不連續的)。
每個數據單元有兩部分組成,一個是數據域,存儲數據值;另一個是指針域,指向下一個數據單元。這樣的數據單元叫做結點。
當多個結點通過指針指向,關聯起來,就形成了一個鏈,即鏈表。
單鏈表
鏈表可分為單鏈表、雙鏈表、循環鏈表。
本文先介紹單鏈表。
單鏈表就是沿着單方向的鏈表。例如:A->B->C->D->... 只能順序的連下去,即可以從A往下找其他元素,但是反之則不行。
單鏈表結點的結構可表示如下:
typedef int ElemType; typedef struct LNode { ElemType data; struct LNode* next; } LNode, *LinkList; |
基本算法
插入結點
假設要在單鏈表的a結點和b結點之間插入一個值為x的新結點。
如下圖所示,指針s指向一個值為x的結點,為了插入s。
首先讓s的next指針指向b,即s->next = p->next;
然后,讓a的next指針指向s,即p->next = s;
刪除結點
假設要刪除單鏈表中的b結點。
首先,找到b結點前面的結點a。
如下圖所示,p指針指向a結點。b的下一個結點就是p->next->next。
所以,只要讓p的next指針跳過b結點,指向b的下一個結點就OK了,即p->next = p->next->next;
參考代碼
以下為本人實現的單鏈表的基本操作。歡迎指正。本人的編譯環境為Visual Studio2010,C語言。
基本操作
/*********************************************************************************************************************** [單鏈表操作] [1] destroyList, 銷毀單鏈表 [2] initList, 初始化一個帶頭結點的空單鏈表,如果傳入一個不為空的單鏈表,將被重置 [3] insertElem, 在單鏈表中第 i 個位置插入元素 elem [4] removeElem, 在單鏈表中移除第 pos 個元素,並由 elem 返回其值 [5] createList, 根據數組 elems 構建一個單鏈表 [6] isEmptyList, 判斷單鏈表是否為空 [7] getElem, 獲取單鏈表上位置為 pos 的元素 [8] locateElem, 獲取元素 elem 在單鏈表上第一次出現的位置,如果不存在返回 -1 [9] getLength, 獲取單鏈表長度 [10] printList, 打印整個單鏈表 [11] reverseList, 反轉單鏈表 ***********************************************************************************************************************/ #include <stdio.h> #include <stdlib.h>
/*********************************************************************************************************************** 第一部分,數據結構、宏定義 ***********************************************************************************************************************/ #define MAX 5
typedef enum { OK = 0, ERROR = 1 } STATUS_EN;
typedef enum { TRUE = 0, FALSE = -1 } BOOL;
typedef int ElemType; typedef struct LNode { ElemType data; struct LNode* next; } LNode, *LinkList;
/*********************************************************************************************************************** 第二部分,函數實現 ***********************************************************************************************************************/
/******************************************************************************* Funtion : [1] destroyList Description : 銷毀單鏈表 Input : struct LNode **ppHead Output : struct LNode **ppHead Return Value : STATUS_EN(OK/ERROR) Author : VictorZhang Date : 2015-03-30 *******************************************************************************/ void destroyList(struct LNode **ppHead) { LNode *p = *ppHead; LNode *q = p->next;
// 先遍歷刪除所有元素 while (p && p->next) { q = p->next; p = q->next; free(q); q = NULL; }
// 最后釋放頭結點 free(*ppHead); *ppHead = NULL; }
/******************************************************************************* Funtion : initList Description : 初始化一個帶頭結點的空單鏈表,如果傳入一個不為空的單鏈表, 將被重置 Input : struct LNode **ppHead Output : struct LNode **ppHead Return Value : STATUS_EN(OK/ERROR) Author : VictorZhang Date : 2015-03-30 *******************************************************************************/ STATUS_EN initList(struct LNode **ppHead) { if (*ppHead) destroyList(ppHead);
LNode *p = (LNode*)malloc(sizeof(LNode)); p->next = NULL; p->data = 0; *ppHead = p; return OK; }
/******************************************************************************* Funtion : insertElem Description : 在單鏈表中第 i 個位置插入元素 elem Input : struct LNode **ppHead, const int pos, const ElemType elem Output : struct LNode **ppHead Return Value : STATUS_EN(OK/ERROR) Author : VictorZhang Date : 2015-03-30 *******************************************************************************/ STATUS_EN insertElem(struct LNode **ppHead, const int pos, const ElemType elem) { LNode *p = *ppHead; LNode *s = NULL;
// 尋找鏈表當前最后一個結點 int i = 0; while (p && i < pos) { p = p->next; i++; }
// 未找到末尾結點 if (!p || i > pos) return ERROR;
// 生成新結點 s = (LNode*) malloc (sizeof(LNode)); if (!s) return ERROR;
// 插入單鏈表中 s->data = elem; s->next = p->next; p->next = s;
return OK; }
/******************************************************************************* Funtion : removeElem Description : 在單鏈表中移除第 pos 個元素,並由 elem 返回其值 Input : struct LNode **ppHead, const int pos, ElemType *pElem Output : struct LNode **ppHead, ElemType *pElem Return Value : STATUS_EN(OK/ERROR) Author : VictorZhang Date : 2015-03-30 *******************************************************************************/ STATUS_EN removeElem(struct LNode **ppHead, const int pos, ElemType *pElem) { LNode *p = *ppHead; LNode *q = NULL; int i = 0; while (p && p->next && i < pos) { p = p->next; i++; }
// 刪除位置不合理 if (!(p->next) || i > pos) return ERROR;
// 刪除並釋放結點 q = p->next; p->next = q->next; *pElem = q->data; free(q); return OK; }
/******************************************************************************* Funtion : createList Description : 根據數組 elems 構建一個單鏈表 Input : struct LNode **ppHead, const ElemType elems[], const int n Output : struct LNode **ppHead Return Value : STATUS_EN(OK/ERROR) Author : VictorZhang Date : 2015-03-30 *******************************************************************************/ STATUS_EN createList(struct LNode **ppHead, const ElemType elems[], const int n) { int i = 0; STATUS_EN statu = OK;
// 按序將數組元素插入到單鏈表尾部 for (i = 0; i < n; i++) { statu = insertElem(ppHead, i, elems[i]); if (OK != statu) return statu; }
return OK; }
/******************************************************************************* Funtion : isEmptyList Description : 判斷單鏈表是否為空 Input : struct LNode *pHead Output : N/A Return Value : BOOL Author : VictorZhang Date : 2015-03-30 *******************************************************************************/ BOOL isEmptyList(struct LNode *pHead) { if (NULL == pHead || NULL == pHead->next) return TRUE; else return FALSE; }
/******************************************************************************* Funtion : getElem Description : 獲取單鏈表上位置為 pos 的元素 Input : struct LNode *pHead, const int pos, ElemType *pElem Output : ElemType *pElem Return Value : STATUS_EN(OK/ERROR) Author : VictorZhang Date : 2015-03-30 *******************************************************************************/ STATUS_EN getElem(struct LNode *pHead, const int pos, ElemType *pElem) { int i = 0; LNode *p = pHead->next; while (p && i <= pos) { if (i == pos) { *pElem = p->data; return OK; } else { p = p->next; i++; } } return ERROR; }
/******************************************************************************* Funtion : locateElem Description : 獲取元素 elem 在單鏈表上第一次出現的位置,如果不存在返回 -1 Input : struct LNode *pHead, const ElemType elem Output : N/A Return Value : int Author : VictorZhang Date : 2015-03-30 *******************************************************************************/ int locateElem(struct LNode *pHead, const ElemType elem) { int pos = 0; LNode *p = pHead->next; while (p) { if (p->data == elem) { return pos; } else { pos++; p = p->next; } } return -1; }
/******************************************************************************* Funtion : getLength Description : 獲取單鏈表長度 Input : struct LNode *pHead Output : N/A Return Value : int Author : VictorZhang Date : 2015-04-02 *******************************************************************************/ int getLength(struct LNode *pHead) { if (NULL == pHead || NULL == pHead->next) { return 0; }
int i = 0; LNode *p = pHead->next; while (p) { p = p->next; i++; } return i; }
/******************************************************************************* Funtion : printList Description : 打印整個單鏈表 Input : struct LNode *pHead Output : N/A Return Value : N/A Author : VictorZhang Date : 2015-04-02 *******************************************************************************/ void printList(struct LNode *pHead) { if (NULL == pHead || NULL == pHead->next) { printf("LinkList is empty\n"); return; } LNode *p = pHead->next; printf("LinkList:"); while (p) { printf(" %d", p->data); p = p->next; } printf("\n"); }
/******************************************************************************* Funtion : reverseList Description : 反轉單鏈表 Input : struct LNode **ppHead Output : struct LNode **ppHead Return Value : N/A Author : VictorZhang Date : 2015-04-02 *******************************************************************************/ void reverseList(struct LNode **ppHead) { if (NULL == *ppHead || NULL == (*ppHead)->next) { return; }
LNode *prev = NULL; LNode *cur = (*ppHead)->next; LNode *next = NULL;
while (cur) { next = cur->next; cur->next = prev; prev = cur; cur = next; } (*ppHead)->next = prev; } |
測試例部分
/***********************************************************************************************************************
第三部分,測試例
***********************************************************************************************************************/
void testCase0() {
printf("================== testCase0 ==================\n");
int len = 0;
BOOL bFlag = FALSE;
ElemType A[MAX] = {4,5,2,1,3};
struct LNode *pHead = NULL;
// 初始化鏈表
initList(&pHead);
printf("Init List\n");
// 獲取鏈表長度
len = getLength(pHead);
printf("Length of List is %d\n", len);
// 根據一個數組來創建單鏈表
createList(&pHead, A, MAX);
printf("After create List:\n");
printList(pHead);
// 獲取鏈表長度
len = getLength(pHead);
printf("Length of List is %d\n", len);
// 判斷單鏈表是否為空
bFlag = isEmptyList(pHead);
if (bFlag) {
printf("It is a empty List.\n");
} else {
printf("It is not a empty List.\n");
}
// 銷毀鏈表
printf("Destroy List\n");
destroyList(&pHead);
// 獲取鏈表長度
len = getLength(pHead);
printf("Length of List is %d\n", len);
// 判斷單鏈表是否為空
bFlag = isEmptyList(pHead);
if (bFlag) {
printf("It is a empty List.\n");
} else {
printf("It is not a empty List.\n");
}
}
void testCase1() {
printf("================== testCase1 ==================\n");
STATUS_EN statu;
ElemType A[MAX] = {4,5,2,1,3};
struct LNode *pHead = NULL;
// 初始化鏈表
initList(&pHead);
printf("Init List\n");
createList(&pHead, A, MAX);
printf("After create List:\n");
printList(pHead);
// 在尾部位置嘗試插入元素
statu = insertElem(&pHead, 5, 9);
printf("Insert element:\n");
if (OK != statu) {
printf("Insert failed!\n");
} else {
printList(pHead);
}
// 在頭部位置嘗試插入元素
statu = insertElem(&pHead, 0, 2);
if (OK != statu) {
printf("Insert failed!\n");
} else {
printList(pHead);
}
// 中間位置嘗試插入元素
statu = insertElem(&pHead, 3, 7);
if (OK != statu) {
printf("Insert failed!\n");
} else {
printList(pHead);
}
// 嘗試在不合理的位置上插入元素
statu = insertElem(&pHead, 99, 15);
if (OK != statu) {
printf("Insert failed!\n");
} else {
printList(pHead);
}
}
void testCase2() {
printf("================== testCase2 ==================\n");
STATUS_EN statu;
ElemType elem;
ElemType A[MAX] = {4,5,2,1,3};
struct LNode *pHead = NULL;
// 初始化鏈表
initList(&pHead);
printf("Init List\n");
createList(&pHead, A, MAX);
printf("After create List:\n");
printList(pHead);
// 嘗試移除尾部位置的元素
statu = removeElem(&pHead, 4, &elem);
printf("Remove element pos(%d)\n", 4);
if (OK != statu) {
printf("Remove failed!\n");
} else {
printList(pHead);
}
// 嘗試移除頭部位置的元素
statu = removeElem(&pHead, 0, &elem);
printf("Remove element pos(%d)\n", 0);
if (OK != statu) {
printf("Remove failed!\n");
} else {
printList(pHead);
}
// 嘗試移除中間位置的元素
statu = removeElem(&pHead, 1, &elem);
printf("Remove element pos(%d)\n", 1);
if (OK != statu) {
printf("Remove failed!\n");
} else {
printList(pHead);
}
// 嘗試移除不合理位置的元素
statu = removeElem(&pHead, 11, &elem);
printf("Remove element pos(%d)\n", 11);
if (OK != statu) {
printf("Remove failed!\n");
} else {
printList(pHead);
}
}
void testCase3() {
printf("================== testCase3 ==================\n");
int pos = 4;
STATUS_EN statu;
ElemType elem;
ElemType A[MAX] = {4,5,2,1,3};
struct LNode *pHead = NULL;
// 初始化鏈表
initList(&pHead);
printf("Init List\n");
createList(&pHead, A, MAX);
printf("After create List:\n");
printList(pHead);
// 獲取指定位置上的元素
statu = getElem(pHead, pos, &elem);
if (OK != statu) {
printf("Get element failed!\n");
} else {
printf("The elem in pos(%d) is %d\n", pos, elem);
}
// 查找元素在單鏈表中第一次出現的位置
elem = 4;
pos = locateElem(pHead, elem);
printf("%d is in pos(%d) of List\n", elem, pos);
elem = 9;
pos = locateElem(pHead, elem);
printf("%d is in pos(%d) of List\n", elem, pos);
}
void testCase4() {
printf("================== testCase4 ==================\n");
ElemType A[MAX] = {4,5,2,1,3};
struct LNode *pHead = NULL;
// 初始化鏈表
initList(&pHead);
printf("Init List\n");
createList(&pHead, A, MAX);
printf("After create List:\n");
printList(pHead);
// 反轉單鏈表
reverseList(&pHead);
printf("Reverse List:\n");
printList(pHead);
}
int main() {
testCase0();
testCase1();
testCase2();
testCase3();
testCase4();
return 0;
}
參考資料
《數據結構》(C語言版) ,嚴蔚敏、吳偉民
《數據結構習題與解析》(B級第3版),李春葆、喻丹丹
相關閱讀
歡迎閱讀 程序員的內功——數據結構和算法 系列