数据结构:链表实现增删查改的基本功能内含详细代码,建议收藏
Posted lxkeepcoding
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构:链表实现增删查改的基本功能内含详细代码,建议收藏相关的知识,希望对你有一定的参考价值。
目录
前言
hello,大家好,这期文章继续用来更新数据结构方面的知识——链表。。这期文章依旧是对之前的文章数据结构:红玫瑰与白玫瑰之争的补充,希望对大家有所帮助。我们在之前的文章中已经介绍过顺序表,链表和顺序表有很大的不同,相对于顺序表,链表无疑是更加灵活的。顺序表在实现过程中头部插入数据时需要挪动数据,在增容的过程中也会消耗性能,并且以倍数的方式增容也会造成空间的浪费。链表相对于顺序表而言,也许会做的更好
1. 链表的概念
链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的
通俗理解,链表就是把数据块连接起来,还方便拆卸。可以随时将数据块加入进去,或者拿出去。
3. 链表的结构
在这些链表结构的组合之中,我们最常用的是两种
- 无头单向非循环链表:结构简单,一般不会单独用来存数据。实际中更多是作为其他数据结构的子结构,如哈希桶、图的邻接表等等。另外这种结构在笔试面试中出现很多。
- 带头双向循环链表:结构最复杂,一般用在单独存储数据。实际中使用的链表数据结构,都是带头双向循环链表。另外这个结构虽然结构复杂,但是使用代码实现以后会发现结构会带来很多优势,实现反而简单了
4. 无头单向非循环链表的实现
4.1 创建工程
依旧采用多文件的方式
4.2 定义结构体
typedef int SLDataType;
typedef struct SListNode
{
int data;
struct SListNode *next;
}SLNode;
4.3 创建一个新节点
4.3.1 SList.h 声明
SLNode *BuySLNode(SLDataType x);
4.3.2 SList.c 定义
SLNode *BuySLNode(SLDataType x)
{
SLNode *node = (SLNode*)malloc(sizeof(SLNode));
node->data = x;
node->next = NULL;
return node;
}
创建一个新节点会在增加操作中频繁使用,所以我们单独定义出一个函数来。创建新节点的思路就是开辟一块空间,空间里存放想要存放的数据,并使这块空间指向NULL。
4.4 头插尾插操作
注意,在头插尾插操作中,我们传递的都是二级指针。因为节点本身就是指针,我们知道按地址传参才可以改变原值,由于plist本身就是指针,要想改变它,所以我们需要传二级指针**pplist。
4.4.1 SList.h 声明
void SListPushFront(SLNode **pplist, SLDataType x);
void SListPushBack(SLNode **pplist, SLDataType x);
4.3.2 SList.c 定义
void SListPushFront(SLNode **pplist, SLDataType x)
{
SLNode*newnode = BuySLNode(x);
newnode->next = *pplist;
*pplist = newnode;
}
void SListPushBack(SLNode **pplist, SLDataType x)
{
SLNode*newnode = BuySLNode(x);
//空和非空
if (*pplist == NULL)
{
*pplist = newnode;
}
else
{
//先遍历找尾
SLNode *tail = *pplist;
while (tail->next != NULL)
{
tail = tail->next;
}
SLNode *newnode = BuySLNode(x);
tail->next = newnode;
}
}
头插相对于尾插更好实现,只需要在头节点前链接上新节点就好。但是尾插则需要考虑的更多一些,实现我们要判断链表此时是否为空,如果是空则不需尾插,如果非空,我们则需要首先找到链表的尾,在尾上插入新的节点。尾插动画演示如下:
4.5 头删尾删操作
4.5.1 SList.h 声明
void SListPopFront(SLNode **pplist);
void SListPopBack(SLNode **pplist);
4.5.2 SList.c 定义
void SListPopFront(SLNode **pplist)
{
if (*pplist == NULL)
{
return;
}
else
{
SLNode *next= (*pplist)->next;
free(*pplist);
*pplist = next;
}
}
void SListPopBack(SLNode **pplist)
{
if (*pplist == NULL)
{
return;
}
else if ((*pplist)->next == NULL)
{
free(*pplist);
*pplist = NULL;
}
else
{
SLNode *prev = NULL;
SLNode *tail = *pplist;
while (tail->next != NULL)
{
prev = tail;
tail = tail->next;
}
free(tail);
tail = NULL;
prev->next = NULL;
}
}
头删只需要去除第一个节点,注意判断如果链表为空则不需要操作。对于尾删,依旧是要先找到尾部,还要找到尾部节点的前一个节点prev,将尾部节点释放,然后将prev置为新的尾节点。尾删动画演示如下:
4.6 查找操作
查找操作要返回该节点的指针,注意考虑空链表的情况。
4.6.1 SList.h 声明
SLNode*SListFind(SLNode **pplist, SLDataType x);
4.6.2 SList.c 定义
SLNode*SListFind(SLNode **pplist, SLDataType x)
{
SLNode *cur = *pplist;
while (cur)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
4.7 修改操作
修改操作时在查找的基础上进行的,找到要修改的值的节点,然后进行修改。注意要考虑找不到要修改的数据的情况。
4.7.1 SList.h 声明
void SListModify(SLNode *plist, SLDataType x, SLDataType y);
4.7.2 SList.c 定义
void SListModify(SLNode *plist, SLDataType x, SLDataType y)
{
SLNode *pos = SListFind(&plist, x);
if (pos)
{
pos->data = y;
}
else
{
printf("没有该数字,无法修改\\n");
}
}
4.8 前插后插操作
前插后插比头插尾插更复杂一些。尤其是前插操作,需要考虑的东西会多一点。后插我们只需要在位置后加入一个新的节点,而前插则需要考虑该位置是不是头节点。如果不是头节点,还需要找到该位置并且需要找到该位置的前一个节点,将新节点插入其中。下面通过动画演示非首元素前插的情况:
4.8.1 SList.h 声明
void SListInsertAfter(SLNode *pos, SLDataType x);
void SListInsertBefore(SLNode **pplist,SLNode *pos, SLDataType x);
4.8.2 SList.c 定义
void SListInsertAfter(SLNode *pos, SLDataType x)
{
assert(pos);
SLNode *newnode = BuySLNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
void SListInsertBefore(SLNode **pplist, SLNode *pos, SLDataType x)
{
assert(pos);
SLNode *newnode = BuySLNode(x);
if (pos == *pplist)
{
newnode->next = pos;
*pplist = newnode;
}
else
{
SLNode *prev = NULL;
SLNode *cur = *pplist;
while (cur != pos)
{
prev = cur;
cur = cur->next;
}
prev->next = newnode;
newnode->next = pos;
}
}
4.9 现删后删操作
对于删除操作,我们再这里实现删除指定位置后的节点和指定位置的节点,因为删除指定位置之前的节点实现起来非常麻烦且意义不大。因为我们可以通过改变指定位置来实现删除前一个节点。删除指定位置后一个节点需要考虑是否为空链表。删除当前位置节点则需要考虑当前位置是否为首节点。我们在这里用动画演示删除非首节点当前节点的操作:
4.9.1 SList.h 声明
void SListEraseAfter(SLNode*pos);
void SListEraseCur(SLNode **pplist,SLNode*pos);
4.9.2 SList.c 定义
void SListEraseAfter(SLNode*pos)
{
assert(pos);
if (pos->next == NULL)
{
return;
}
else
{
SLNode*next = pos->next;
pos->next = next->next;
free(next);
next = NULL;
}
}
void SListEraseCur(SLNode **pplist,SLNode*pos)
{
if (pos == *pplist)
{
return;
}
else
{
SLNode *prev = NULL;
SLNode *cur = *pplist;
while (cur != pos)
{
prev = cur;
cur = cur->next;
}
prev->next = pos->next;
free(cur);
cur = NULL;
}
}
4.10 完整代码展示及运行
4.10.1 SeqList.h
#include<stdio.h>
#include<malloc.h>
#include<assert.h>
typedef int SLDataType;
typedef struct SListNode
{
int data;
struct SListNode *next;
}SLNode;
//单向+带头+不循环
void SListPrint(SLNode *plist);
SLNode *BuySLNode(SLDataType x);
//二级指针
void SListPushFront(SLNode **pplist, SLDataType x);
void SListPushBack(SLNode **pplist, SLDataType x);
void SListInsertAfter(SLNode *pos, SLDataType x);
void SListInsertBefore(SLNode **pplist,SLNode *pos, SLDataType x);
void SListPopFront(SLNode **pplist);
void SListPopBack(SLNode **pplist);
void SListEraseAfter(SLNode*pos);
void SListEraseCur(SLNode **pplist,SLNode*pos);
SLNode*SListFind(SLNode **pplist, SLDataType x);
void SListModify(SLNode *plist, SLDataType x, SLDataType y);
4.10.2 SeqList.c
#include"SList.h"
void SListPrint(SLNode *plist)
{
SLNode *cur = plist;
while (cur != NULL)
{
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\\n");
}
SLNode *BuySLNode(SLDataType x)
{
SLNode *node = (SLNode*)malloc(sizeof(SLNode));
node->data = x;
node->next = NULL;
return node;
}
void SListPushFront(SLNode **pplist, SLDataType x)
{
SLNode*newnode = BuySLNode(x);
newnode->next = *pplist;
*pplist = newnode;
}
void SListPushBack(SLNode **pplist, SLDataType x)
{
SLNode*newnode = BuySLNode(x);
//空和非空
if (*pplist == NULL)
{
*pplist = newnode;
}
else
{
//先遍历找尾
SLNode *tail = *pplist;
while (tail->next != NULL)
{
tail = tail->next;
}
SLNode *newnode = BuySLNode(x);
tail->next = newnode;
}
}
void SListPopFront(SLNode **pplist)
{
if (*pplist == NULL)
{
return;
}
else
{
SLNode *next= (*pplist)->next;
free(*pplist);
*pplist = next;
}
}
void SListPopBack(SLNode **pplist)
{
if (*pplist == NULL)
{
return;
}
else if ((*pplist)->next == NULL)
{
free(*pplist);
*pplist = NULL;
}
else
{
SLNode *prev = NULL;
SLNode *tail = *pplist;
while (tail->next != NULL)
{
prev = tail;
tail = tail->next;
}
free(tail);
tail = NULL;
prev->next = NULL;
}
}
SLNode*SListFind(SLNode **pplist, SLDataType x)
{
SLNode *cur = *pplist;
while (cur)
{
if (cur->data == x)
{
return cur;
}
cur = cur->next;
}
return NULL;
}
void SListModify(SLNode *plist, SLDataType x, SLDataType y)
{
SLNode *pos = SListFind(&plist, x);
if (pos)
{
pos->data = y;
}
else
{
printf("没有该数字,无法修改\\n");
}
}
void SListInsertAfter(SLNode *pos, SLDataType x)
{
assert(pos);
SLNode *newnode = BuySLNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
void SListInsertBefore(SLNode **pplist, SLNode *pos, SLDataType x)
{
assert(pos);
SLNode *newnode = BuySLNode(x);
if (pos == *pplist)
{
newnode->next = pos;
*pplist = newnode;
}
else
{
SLNode *prev = NULL;
SLNode *cur = *pplist;
while (cur != pos)
{
prev = cur;
cur = cur->next;
}
prev->next = newnode;
newnode->next = pos;
}
}
void SListEraseAfter(SLNode*pos)
{
assert(pos);
if (pos->next == NULL)
{
return;
}
else
{
SLNode*next = pos->next;
pos->next = next->next;
free(next);
next = NULL;
}
}
void SListEraseCur(SLNode **pplist,SLNode*pos)
{
if (pos == *pplist)
{
return;
}
else
{
SLNode *prev = NULL;
SLNode *cur = *pplist;
while (cur != pos)
{
prev = cur;
cur = cur->next;
}
prev->next = pos->next;
free(cur);
cur = NULL;
}
}
4.10.3 test.c
#include"SList.h"
void TestOne()
{
SLNode *plist = NULL;
SListPushBack(&plist, 1);
SListPushBack(&plist, 2);
SListPushBack(&plist, 3);
SListPushBack以上是关于数据结构:链表实现增删查改的基本功能内含详细代码,建议收藏的主要内容,如果未能解决你的问题,请参考以下文章