目录
引言
单链表是一种常见的线性数据结构,用于存储一系列的数据元素。在本文中,我们将深入探讨单链表的基本操作,包括创建节点、销毁链表、遍历、插入和删除等,同时结合提供的代码来说明这些操作的实际应用。
一、链表节点的定义和初始化
首先,我们需要了解链表节点的定义,每个节点包含数据域和指针域,数据域用于存储数据元素,指针域用于指向下一个节点。代码中的SLNode结构体就定义了这样一个节点,并提供了创建节点的函数BuyListNode。
typedef int SLDataType;
typedef struct SLNode
{
SLDataType data;
struct SLNode* next;
}SLNode;
SLNode* BuyListNode(SLDataType x)//创建节点
{
SLNode* newnode = (SLNode*) malloc(sizeof(SLNode));
if (newnode == nullptr)
{
perror("malloc failed");
exit(-1);
}
newnode->data = x;
newnode->next = nullptr;
return newnode;
}
二、链表的基本操作
1.头插法和尾插法
通过SLPushFront和SLPushBack函数,可以在链表的头部和尾部插入新的节点,这两种方法是链表插入操作中的常见方式。
代码示例如下:
头插法:
void SLPushFront(SLNode** pphead, SLDataType x)//头插
{
assert(pphead);
SLNode* newnode = BuyListNode(x);
if (*pphead == nullptr)
{
*pphead = newnode;
}
else
{
newnode->next = *pphead;
*pphead = newnode;
}
}
尾插法:
void SLPushBack(SLNode** pphead, SLDataType x)//尾插
{
assert(pphead);
SLNode* newnode = BuyListNode(x);
if (*pphead == nullptr)
{
*pphead = newnode;
}
else
{
SLNode* tail = *pphead;
while (tail->next != nullptr)
{
tail = tail->next;
}
tail->next = newnode;
}
}
2.头删和尾删
使用SLPopFront和SLPopBack函数可以分别删除链表的头节点和尾节点。
代码示例如下:
头删:
void SLPopFront(SLNode** pphead)//头删
{
assert(pphead);
//空
assert(*pphead);
//非空
SLNode* head = (*pphead)->next;
free(*pphead);
*pphead = head;
}
尾删:
void SLPopBack(SLNode** pphead)//尾删
{
assert(pphead);
//1.空
assert(*pphead);
//2.一个节点
if ((*pphead)->next == nullptr)
{
free(pphead);
pphead = nullptr;
}
//3.一个以上
else
{
SLNode* tail = *pphead;
SLNode* tailprev = nullptr;
while (tail->next != nullptr)
{
tailprev = tail;
tail = tail->next;
}
free(tail);
tailprev->next = nullptr;
}
}
3.查找操作
SLFind函数可以用来查找链表中是否存在某个特定的值。
代码示例如下:
SLNode* SLFind(SLNode* phead, SLDataType x)//查找某个值x
{
while (phead != nullptr)
{
if (phead->data == x)
return phead;
phead = phead->next;
}
return nullptr;
}
4.插入和删除任意位置
通过SLInsert和SLErase函数可以在任意位置插入和删除节点,这两个操作也是链表常见的操作之一。
代码示例如下:
在pos前插入x:
void SLInsert(SLNode** pphead, SLNode* pos, SLDataType x)
{
assert(pos);
assert(pphead);
SLNode* newnode = BuyListNode(x);
if (pos == *pphead)
{
SLPushFront(pphead, x);
}
else
{
SLNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = newnode;
newnode->next = pos;
}
}
在pos后插入x:
void SLInsertAfter(SLNode** pphead, SLNode* pos, SLDataType x)
{
assert(pphead);
assert(pos);
SLNode* newnode = BuyListNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
删除pos位置:
void SLErase(SLNode** pphead, SLNode* pos)
{
assert(pphead);
assert(pos);
if (pos == *pphead)
{
SLPopFront(pphead);
}
else
{
SLNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
}
}
删除pos后一个位置:
void SLEraseAfter(SLNode** pphead, SLNode* pos)
{
assert(pphead);
assert(pos);
assert(pos->next);//检查pos是否为尾节点
SLNode* posNext = pos->next;
pos->next = posNext->next;
free(posNext);
}
5.遍历链表
SLPrint函数用于遍历整个链表,并将链表中的数据打印出来。
代码示例如下:
void SLPrint(SLNode* phead)
{
SLNode* cur = phead;
while (cur != nullptr)
{
std::cout << cur->data << "->";
cur = cur->next;
}
std::cout << "nullptr" << std::endl;
}
三、链表的销毁
最后,当链表使用完毕时,需要使用Destroy函数来销毁整个链表,释放内存空间,避免内存泄漏。
代码示例如下:
void Destroy(SLNode** pphead)//销毁链表
{
assert(pphead);
SLNode* cur = *pphead;
while (cur)
{
SLNode* next = cur->next;
free(cur);
cur = next;
}
*pphead = nullptr;
}
总结
在这篇博客中,我们详细介绍了单链表的各种基本操作,包括创建和销毁链表、插入和删除节点、遍历链表以及查找特定值。通过这些操作,可以在实际应用中灵活地操作链表结构。希望这篇博客能够帮助读者更好地理解和应用单链表。