数据结构的第二个较为关键的就是链表了,今天给大家带来有关链表的操作,话不多说,我们直接开始
给链表开辟的空间可以是不连续的,因为每个节点都要存储一个数据和下一个节点的指针,这样就可以很容易的找到下一个节点的数据。
链表其实是和顺序表是优劣互补的,我们想象一下如果要删除顺序表的某个中间元素是不是非常的麻烦,因为还要将后面的元素挨个挪到前面,于是,链表这种结构就可以实现简单的删除,只需要将上一个节点的next指针指向下一个元素即可。
同样我们把要存储的数据类型重定义一下
typedef int SLTDataType;
之后就是创建一个节点了,这个节点要包括一个要存储的数据和指向下一个节点的指针,指针的类型是指向这个节点类型的。
typedef struct SListNode {
SLTDataType data;
struct SListNode* next;
}SLTNode;
每一个结点创建好了,那我们怎么创建一个链表呢,或者说我们通过什么去访问这个链表呢,我们只需要在主函数中创建一个指针去指向第一个结点不就可以了吗。
SLTNode* plist=NULL;
这里的plist只是一个指针,它不是一个节点,它里面存放的就是第一个节点的地址,有了第一个节点就可以访问第二个节点,后面的也就都可以访问了
我们还需要一个函数去创建一个新结点,因为插入数据我们肯定需要一个新节点,创建新结点我们需要动态开辟新内存,我们把它封装成一个函数,并且返回新节点的地址。
SLTNode* BuySListNode(SLTDataType x) {
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL) {
perror("malloc failed");//打印错误信息
exit(-1);//异常退出
}
newnode->data = x;//把要存储的数据放到新结点
newnode->next = NULL;
return newnode;
}
之后呢我们肯定就要插入数据了,分为头插和尾插,我们先来实现一下头插
我们头插就是把元素插入到第一个位置,谁记录着第一个位置的地址呢?就是plist,所以我们要改掉plist的值,改它的值就要传它的地址,而plist本来就是一个地址嘛,所以参数类型是一个二级指针。
void SLTPushFront(SLTNode** pphead, SLTDataType x) {
assert(pphead);
SLTNode* newnode = BuySListNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
尾插的话我们要看一开始链表是否为空,如果是空的话就要改掉plist的值,不是空我们再找最后一个值就可以了
void SLTPushBack(SLTNode** pphead, SLTDataType x) {
assert(pphead);
SLTNode* newnode = BuySListNode(x);
if (*pphead == NULL) {
*pphead = newnode;
}
else {
SLTNode* tail = *pphead;
while (tail->next != NULL) {
tail = tail->next;
}
tail->next = newnode;
}
}
有插入就会有删除,也是分为头删和尾删,我们先来实现一下头删
删除的话我们要先考虑链表为空的话不能删,我们还是要改掉plist的值
void SLTPopFront(SLTNode** pphead) {
assert(pphead);
assert(*pphead);
SLTNode* tmp = (*pphead)->next;
free(*pphead);
*pphead = tmp;
}
尾删的话要看是不是只有一个元素,只有一个元素的话就是头删了
void SLTPopBack(SLTNode** pphead) {
assert(pphead);
assert(*pphead);
if ((*pphead)->next == NULL) {//链表中只有一个元素
free(*pphead);
*pphead = NULL;
}
else {//tailprev为了记住tail之前的位置
SLTNode* tail = *pphead;
SLTNode* tailprev = NULL;
while (tail->next != NULL) {
tailprev = tail;
tail = tail->next;
}
free(tail);
tailprev->next = NULL;
//当然我们不定义tailprev也可以
/*
SLTNode*tail=*pphead;
while(tail->next->next!=NULL){
tail=tail->next
}
free(tail->next);
tail->next=NULL;
*/
}
}
在一系列的操作之后,我们总要打印一下链表吧,于是打印函数
void SLTPrint(SLTNode* phead) {
SLTNode* cur = phead;
while (cur != NULL) {
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
然后我们如何查找一个数据呢,我们还要写一个查找的函数
我们查找到的话要返回这个结点的地址,查找不到呢就返回一个空指针
SLTNode* SLTFind(SLTNode* phead, SLTDataType x) {
assert(phead);
SLTNode* cur = phead;
while (cur != NULL) {
if (cur->data == x) {
return cur;
}
cur = cur->next;
}
return NULL;
}
有了在头和尾的操作,如果我们想在任意位置插入或删除的话也是可以的
比如说我们在给定的位置前插入
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x) {
assert(pphead);
assert(pos);
if (*pphead == pos) {
//SLTPushFront(pphead, x);
SLTNode* newnode = BuySListNode(x);
newnode->next = pos;
*pphead = newnode;
}
else {
SLTNode* newnode = BuySListNode(x);
SLTNode* cur = *pphead;
while (cur->next != pos) {
cur = cur->next;
}
newnode->next = pos;
cur->next = newnode;
}
}
我们在给定的位置后插入
void SLTInsertAfter(SLTNode* pos, SLTDataType x) {
assert(pos);
SLTNode* newnode = BuySListNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
删除给定位置的元素
void SLTErase(SLTNode** pphead, SLTNode* pos) {
assert(pphead);
assert(pos);
if (*pphead == pos) {
//SLTPopFront(pphead);
*pphead = pos->next;
free(pos);
}
else {
SLTNode* cur = *pphead;
while (cur->next != pos) {
cur = cur->next;
}
cur->next = pos->next;
free(pos);
}
}
删除给定位置后的元素
void SLTEraseAfter(SLTNode* pos) {
assert(pos);
assert(pos->next);
SLTNode* tmp = pos->next->next;
free(pos->next);
pos->next = tmp;
}
最后就是销毁每个结点了
void SLTDestroy(SLTNode** pphead) {
assert(pphead);
SLTNode* cur = *pphead;
while (cur) {
SLTNode* next = cur->next;
free(cur);
cur = next;
}
*pphead = NULL;
}
要将每个结点free掉,并且最后要将plist中的值置为NULL
最后是所有代码的呈现
#include
#include
#include
typedef int SLTDataType;
typedef struct SListNode {
SLTDataType data;
struct SListNode* next;
}SLTNode;
void SLTPrint(SLTNode* phead);//打印链表
SLTNode* BuySListNode(SLTDataType x);//创建一个新节点
void SLTPushBack(SLTNode** pphead, SLTDataType x);//尾插一个新节点
void SLTPushFront(SLTNode** pphead, SLTDataType x);//头插一个新节点
void SLTPopBack(SLTNode** pphead);//尾删节点
void SLTPopFront(SLTNode** pphead);//头删节点
SLTNode* SLTFind(SLTNode* phead, SLTDataType x);//在链表中找x的值
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);//在pos位置前插入数据
void SLTInsertAfter(SLTNode* pos, SLTDataType x);//在pos位置后插入数据
void SLTErase(SLTNode** pphead, SLTNode* pos);//删除掉pos位置的结点
void SLTEraseAfter(SLTNode* pos);//删除掉pos位置后面的结点
void SLTDestroy(SLTNode** pphead);//销毁链表,释放空间
#include"SList.h"
void SLTPrint(SLTNode* phead) {
SLTNode* cur = phead;
while (cur!=NULL) {
printf("%d->", cur->data);
cur = cur->next;
}
printf("NULL\n");
}
SLTNode* BuySListNode(SLTDataType x) {
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL) {
perror("malloc failed");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
return newnode;
}
void SLTPushBack(SLTNode** pphead, SLTDataType x) {
assert(pphead);
SLTNode* newnode = BuySListNode(x);
if (*pphead == NULL) {//链表为空的情况下,也就是phead中存放的值为NULL
*pphead = newnode;
}
else {
SLTNode* tail = *pphead;
while (tail->next) {//如果让tail找的话,tail的值为空后就找不回来了
tail = tail->next;
}
tail->next = newnode;
}
}
void SLTPushFront(SLTNode** pphead, SLTDataType x) {
assert(pphead);
SLTNode* newnode = BuySListNode(x);
newnode->next = *pphead;
*pphead = newnode;
}
void SLTPopBack(SLTNode** pphead) {
assert(pphead);
assert(*pphead);//当链表为空时
if ((*pphead)->next == NULL) {//当链表只有一个元素
free(*pphead);
*pphead = NULL;
}
else {//链表有大于一个元素
/*SLTNode* tailPrev = NULL;
SLTNode* tail = *pphead;
while (tail->next != NULL) {
tailPrev = tail;
tail = tail->next;
}
free(tail);
tail = NULL;
tailPrev->next = NULL;*/
SLTNode* tail = *pphead;
while (tail->next->next != NULL) {
tail = tail->next;
}
free(tail->next);
tail->next = NULL;
}
}
void SLTPopFront(SLTNode** pphead) {
assert(pphead);
assert(*pphead);//空
SLTNode* tmp = (*pphead)->next;//非空
free(*pphead);
*pphead = tmp;
}
SLTNode* SLTFind(SLTNode* phead, SLTDataType x) {
SLTNode* cur = phead;
while (cur) {
if (cur->data == x)
return cur;
cur = cur->next;
}
return NULL;
}
void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x) {
assert(pphead);
assert(pos);
if (pos == *pphead) {
/*SLTNode* newnode = BuySListNode(x);
newnode->next = *pphead;
*pphead = newnode;*/
SLTPushFront(pphead, x);
}
else {
SLTNode* prev = *pphead;
while (prev->next != pos) {
prev = prev->next;
}
SLTNode* newnode = BuySListNode(x);
newnode->next = pos;
prev->next = newnode;
}
}
void SLTInsertAfter(SLTNode* pos, SLTDataType x) {
assert(pos);
SLTNode* newnode = BuySListNode(x);
newnode->next = pos->next;
pos->next = newnode;
}
void SLTErase(SLTNode** pphead, SLTNode* pos) {
assert(pphead);
assert(pos);
if (pos == *pphead) {
/*SLTNode* tmp = pos->next;
free(pos);
*pphead = tmp;*/
SLTPopFront(pphead);
}
else {
SLTNode* prev = *pphead;
while (prev->next != pos) {
prev = prev->next;
}
prev->next = pos->next;
free(pos);
}
}
void SLTEraseAfter(SLTNode* pos) {
assert(pos);
assert(pos->next);
SLTNode* posnext = pos->next;
pos->next = posnext->next;
free(posnext);
}
void SLTDestroy(SLTNode** pphead) {
assert(pphead);
SLTNode* cur = *pphead;
while (cur) {
SLTNode* next = cur->next;
free(cur);
cur = next;
}
*pphead = NULL;
}