目录
前言
链表的定义
链表的创建
头插法创建链表
尾插法创建链表
链表的删除
在头部删除元素
在尾部删除元素
查找固定元素
在链表中插入元素
在pos位置前插入
在pos位置后插入
删除链表结点
删除pos位置的结点
删除pos后的结点
链表的销毁
写在最后
前言
前面我们学习了顺序表,顺序表访问元素直接用下标就可以,支持随机访问,随即访问固然强大,但是也会有不足之处,顺序表的插入删除元素需要移动大量的元素,倒是操作很不方便,浪费大量的时间。
接下来我们学习的链表就很好的解决了这个问题,链表中的每一个元素都有对应的地址,通过“链"建立元素之间的逻辑关系。
链表的定义
链表的节点包含存储的元素,以及指向下一个结点的指针,当然链表的元素可以是任意元素,下面仅用int类型进行介绍。
typedef struct ListNode
{
int data;
struct ListNode* next;
}ListNode;
链表的创建
头插法创建链表
头插法创建链表就是每次插入的元素都是在链表的头部进行,利用BuyNewNode(int x)函数申请一个新的结点。
在插入元素时,当时第一个元素时需*pphead = newNode,后面再有元素插入链表时只需更新新结点的next指针和更新*pphead即可。
ListNode* BuyNewNode(int x)
{
ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
if (newNode == NULL)
{
printf("malloc fail\n");
return NULL;
}
newNode->data = x;
newNode->next = NULL;
return newNode;
}
void LSPushFront(ListNode** pphead,int x)
{
assert(pphead);
ListNode* newnode = BuyNewNode(x);
if (*pphead == NULL)
{
*pphead = newnode;
}
else
{
newnode->next = *pphead;
*pphead = newnode;
}
}
尾插法创建链表
尾插法创建链表,故名起义,就是每次插入的元素都是在链表的尾部,和头插法相同的是如果插入的是第一个元素的话就将*pphead的值置为新的结点。
插入其他元素时,因为是尾插法创建链表,所以需要找到链表的最后一个结点,然后将最后一个结点的tail->next指针指向申请的新结点。
void LSPushBack(ListNode** pphead, int x)
{
assert(pphead);
ListNode* newNode = BuyNewNode(x);
if ((*pphead) == NULL)
{
*pphead = newNode;
}
else
{
ListNode* tail = *pphead;
while (tail->next != NULL)
{
tail = tail->next;
}
tail->next = newNode;
}
}
链表的删除
在头部删除元素
在链表的头部删除元素,就是每次从删除链表的头部结点。
删除时需要分类讨论,如果链表只有一个元素的话就直接free()掉*pphead即可,如果链表的元素个数大于一个时,我们需要记录链表的头结点,然后再更新头结点,最后free()掉头结点。
void LSPopFront(ListNode** pphead)
{
assert(pphead);
assert(*pphead);
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
ListNode* pnext = *pphead;
*pphead = (*pphead)->next;
free(pnext);
pnext = NULL;
}
}
在尾部删除元素
与头部删除结点一样,如果是只有一个元素就free()掉*pphead即可。
如果不是只有一个元素的话,我们要分别找到链表的最后一个结点tail以及最后一个结点的前驱结点prev,最后free()掉tail,并将prev->next置为NULL。
void LSPopBack(ListNode** pphead)
{
assert(pphead);
assert(*pphead);
if ((*pphead)->next == NULL)
{
free(*pphead);
*pphead = NULL;
}
else
{
ListNode* tail = *pphead;
while (tail->next != NULL)
{
tail = tail->next;
}
ListNode* prev = *pphead;
while (prev->next != tail)
{
prev = prev->next;
}
free(tail);
tail = NULL;
prev->next = NULL;
}
}
查找固定元素
遍历链表,找到值为x的结点,找到的话返回相应结点信息,查找失败则返回NULL。
ListNode* LSFind(ListNode* phead, int x)
{
assert(phead);
ListNode* pcur = phead;
while (pcur)
{
if (pcur->data == x)
return pcur;
else
pcur = pcur->next;
}
return NULL;
}
在链表中插入元素
在链表中插入元素,分为前插和后插。
在pos位置前插入
在进行插入操作时,要检查pos位置,若是pos和*pphead相等就是前插,直接调用即可。
若不是,则需找到pos位置的前一个结点prev,再申请结点,最后更改指针即可。
void LSInsertFront(ListNode** pphead, ListNode* pos, int x)
{
assert(pphead);
assert(pos);
if (*pphead == pos)
{
LSPushFront(pphead, x);
}
else
{
//ListNode* tail = *pphead;
//ListNode* prev = NULL;
//while (tail != pos)
//{
// prev = tail;
// tail = tail->next;
//}
//ListNode* newnode = BuyNewNode(x);
//newnode->next = tail;
//prev->next = newnode;
ListNode* prev = *pphead;
while (prev)
{
if (prev->next == pos)
{
break;
}
else
{
prev = prev->next;
}
}
ListNode* newNode = BuyNewNode(x);
prev->next = newNode;
newNode->next = pos;
}
}
在pos位置后插入
与前插操作相比,后插操作要简单很多,申请新节点后吗,修改pos指针和新节点指针即可。
void LSInsertBack(ListNode** pphead, ListNode* pos, int x)
{
assert(pphead);
assert(pos);
ListNode* newNode = BuyNewNode(x);
newNode->next = pos->next;
pos->next = newNode;
}
删除链表结点
删除pos位置的结点
删除操作开始时先检查删除位置是不是头结点或者尾结点,如果是的话直接调用头插尾插操作即可完成。
因为是删除pos位置的结点,所以我们得找到pos位置的前一个结点prev,然后修改相应指针并释放pos。
void LSErase(ListNode** pphead, ListNode* pos)
{
assert(pphead);
assert(*pphead);
assert(pos);
if (pos == *pphead)
{
//头删
LSPopFront(pphead);
}
else if (pos->next == NULL)
{
LSPopBack(pphead);
}
else
{
//ListNode* tail = *pphead;
//ListNode* prev = NULL;
//while (tail != pos)
//{
// prev = tail;
// tail = tail->next;
//}
//prev->next = tail->next;
//free(tail);
//tail = NULL;
ListNode* prev = *pphead;
while (prev)
{
if (prev->next == pos)
{
break;
}
else
{
prev = prev->next;
}
}
prev->next = pos->next;
free(pos);
pos = NULL;
}
}
删除pos后的结点
与删除pos位置的结点相比,删除pos后的结点就要简单很多,但是要在删除前检查pos->next不为空,然后记录pos->next指向的结点,然后更新pos->next并且释放要删除的结点。
void LSEraseAfter( ListNode* pos)
{
assert(pos);
assert(pos->next);
ListNode* del = pos->next;
pos->next = del->next;
free(del);
del = NULL;
}
链表的销毁
链表的销毁只能一个一个结点的进行销毁。利用循环迭代可完成。
void LSDestory(ListNode** pphead)
{
//链表的销毁只能一个节点一个节点的进行销毁
assert(pphead);
ListNode* pcur = *pphead;
ListNode* next = NULL;
while (pcur)
{
next = pcur -> next;
free(pcur);
pcur = next;
}
free(*pphead);
free(next);
}
文章来源:https://www.toymoban.com/news/detail-831126.html
写在最后
单链表的操作是有点繁杂,但是经过后续的练习,每个小伙伴都可以掌握,后面我们继续努力!!文章来源地址https://www.toymoban.com/news/detail-831126.html
到了这里,关于数据结构入门——单链表的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!