介绍:
单链表是一种常见的数据结构,它由一系列节点组成,每个节点包含数据和指向下一个节点的指针。本文将介绍如何实现单链表,并提供常用的操作函数。
代码实现:
-
节点的定义和创建 在头文件中,定义了节点结构体SListNode,包含数据成员data和指向下一个节点的指针next。节点的创建通过BuySListNode函数实现,该函数动态申请一个节点,并将数据x赋值给节点的data成员。
// 动态申请一个节点 SListNode* BuySListNode(SLTDateType x) { SListNode* newnode = (SListNode*)malloc(sizeof(SListNode)); if (newnode == NULL) { printf("Fail malloc!\n"); exit(-1); } newnode->data = x; newnode->next = NULL; }
-
单链表的打印 SListPrint函数用于打印单链表的所有节点的数据。它通过遍历链表,依次输出每个节点的数据。
// 单链表打印 void SListPrint(SListNode* plist) { SListNode* cur = plist; while (cur) { printf("%d", cur->data); cur = cur->next; } printf("NULL\n"); }
-
单链表的尾插 SListPushBack函数用于在单链表的末尾插入一个节点。它接受一个指向链表头指针的指针pplist和数据x作为参数。首先判断链表是否为空,如果为空,则直接将新节点插入链表头;否则,找到链表的末尾节点,将新节点插入到其后面。
// 单链表尾插 void SListPushBack(SListNode** pplist, SLTDateType x) { SListNode* newnode = BuySListNode(x); if (*pplist == NULL) { // 改变的结构体的指针,所以要用二级指针 *pplist = newnode; } else { SListNode* tail = *pplist; while (tail) { // 改变的结构体,用结构体的指针即可 tail = tail->next; } tail->next = newnode; } }
-
单链表的头插 SListPushFront函数用于在单链表的头部插入一个节点。它接受一个指向链表头指针的指针pplist和数据x作为参数。首先创建一个新节点,并将数据x赋值给新节点的data成员。然后将新节点的next指针指向原链表的头节点,最后将链表的头指针指向新节点。
// 单链表的头插 void SListPushFront(SListNode** pplist, SLTDateType x) { SListNode* newnode = BuySListNode(x); //把pplist所指向的结构体地址存入newnode->next里面,pplist里面存newnode的地址 newnode->next = *pplist; *pplist = newnode; }
-
单链表的尾删 SListPopBack函数用于删除单链表的末尾节点。它接受一个指向链表头指针的指针pplist作为参数。首先判断链表是否为空,如果为空,则直接返回;否则,找到倒数第二个节点,将其next指针置空,并释放最后一个节点的内存空间。
// 单链表的尾删 void SListPopBack(SListNode** pplist) { //空 assert(*pplist); //一个节点 if ((*pplist)->next == NULL) { free(*pplist); *pplist = NULL; } //多个节点 else { //SLTNode* tailPrev = NULL; //SLTNode* tail = *pphead; //while (tail->next) //{ // tailPrev = tail; // tail = tail->next; //} //free(tail); tail = NULL; //tailPrev->next = NULL; SListNode* tail = *pplist; while (tail->next->next) { tail = tail->next; } free(tail->next); tail->next = NULL; } }
-
单链表的头删 SListPopFront函数用于删除单链表的头节点。它接受一个指向链表头指针的指针pplist作为参数。首先判断链表是否为空,如果为空,则直接返回;否则,将链表的头指针指向第二个节点,并释放原头节点的内存空间。
// 单链表头删 void SListPopFront(SListNode** pplist) { assert(*pplist); SListNode* newhead = (*pplist)->next; free(*pplist); *pplist = newhead; }
-
单链表的查找 SListFind函数用于在单链表中查找指定的数据x。它接受一个指向链表头节点的指针plist和数据x作为参数。通过遍历链表,逐个比较节点的data成员与x的值,如果相等则返回该节点的指针;如果遍历完整个链表都没有找到相等的节点,则返回NULL。
// 单链表查找 SListNode* SListFind(SListNode* plist, SLTDateType x) { SListNode* cur = plist; while (cur) { if (cur->data == x) { return cur; } cur = cur->next; } return NULL; }
-
单链表的插入 SListInsertAfter函数用于在指定位置pos之后插入一个新节点。它接受一个指向位置pos的指针和数据x作为参数。首先创建一个新节点,并将数据x赋值给新节点的data成员。然后将新节点的next指针指向pos节点的next指针指向的节点,最后将pos节点的next指针指向新节点。
// 单链表在pos位置之后插入x void SListInsertAfter(SListNode* pos, SLTDateType x) { assert(pos); SListNode* newnode = BuySListNode(x); newnode->next = pos->next; pos->next = newnode; }
-
SListInsert实现了在单链表中插入一个新节点的功能。函数
SListInsert
接受一个指向链表头指针的指针pphead
、一个指向要插入位置的节点指针pos
,以及要插入的数据x
作为参数。首先,代码使用断言来确保传入的指针参数不为空,以确保代码的安全性。接下来,代码判断如果要插入的位置
pos
是链表的头节点,即*pphead == pos
,则调用SListPushFront
函数将新节点插入到链表的头部。如果要插入的位置
pos
不是链表的头节点,则需要先找到要插入位置的前一个节点。代码使用一个指针prev
来遍历链表,直到找到pos
的前一个节点为止。然后,创建一个新节点newnode
,并将要插入的数据x
赋值给新节点的data
成员。接着,将前一个节点prev
的next
指针指向新节点newnode
,将新节点的next
指针指向要插入的位置pos
,这样就完成了节点的插入操作。通过这段代码,我们可以看出,在单链表中插入一个新节点的操作需要考虑两种情况:要插入的位置是链表的头节点和要插入的位置是其他节点。通过遍历链表找到要插入位置的前一个节点,并进行相应的指针操作,可以实现节点的插入。
//在pos位置插入x void SListInsert(SListNode** pphead, SListNode* pos, SLTDateType x) { assert(pphead); assert(pos); if (*pphead == pos) { SListPushFront(pphead, x); } else { SListNode* prev = *pphead; while (prev->next != pos) { prev = prev->next; } SListNode* newnode = BuySListNode(x); prev->next = newnode; newnode->next = pos; } }
-
单链表的删除 SListEraseAfter函数用于删除指定位置pos之后的节点。它接受一个指向位置pos的指针作为参数。首先将pos节点的next指针指向下下个节点,然后释放被删除节点的内存空间。
// 单链表删除pos位置之后的值 void SListEraseAfter(SListNode* pos) { assert(pos); //检查pos是否是尾节点 assert(pos->next); SListNode* posAfter = pos->next; pos->next = posAfter->next; free(posAfter); posAfter = NULL; }
-
实现了在单链表中删除指定位置的节点的功能。函数
SLTErase
接受一个指向链表头指针的指针pphead
和一个指向要删除位置的节点指针pos
作为参数。首先,代码使用断言来确保传入的指针参数不为空,以确保代码的安全性。
接下来,代码判断如果要删除的位置
pos
恰好是链表的头节点,即pos == *pphead
,则调用SListPopFront
函数将链表的头节点删除。如果要删除的位置
pos
不是链表的头节点,则需要先找到要删除位置的前一个节点。代码使用一个指针prev
来遍历链表,直到找到pos
的前一个节点为止。然后,将前一个节点prev
的next
指针指向要删除位置的下一个节点,以跳过要删除的节点。接着,使用free
函数释放要删除的节点的内存空间,完成节点的删除操作。通过这段代码,我们可以看出,在单链表中删除指定位置的节点的操作需要考虑两种情况:要删除的位置是链表的头节点和要删除的位置是其他节点。通过遍历链表找到要删除位置的前一个节点,并进行相应的指针操作和内存释放,可以实现节点的删除。
//删除pos位置 void SLTErase(SListNode** pphead, SListNode* pos) { assert(pphead); assert(pos); if (pos == *pphead) { SListPopFront(pphead); } else { SListNode* prev = *pphead; while (prev->next != pos) { prev = prev->next; } prev->next = pos->next; free(pos); //pos = NULL; ; } }
通过对上述函数的介绍,我们可以看出,单链表的操作需要注意边界条件的处理。在使用这些函数时,需要确保链表的正确性,并及时释放不再使用的节点的内存空间,以避免内存泄漏。
总结:
本文介绍了单链表的实现和常用操作,包括节点的创建、插入、删除以及查找等。通过对头文件代码的解析,详细说明了每个函数的功能和使用方法。希望本文能够帮助读者理解和应用单链表的基本操作,提升编程能力。