欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 文旅 > 艺术 > 数据结构学习之链表学习:单链表

数据结构学习之链表学习:单链表

2025/5/18 22:38:53 来源:https://blog.csdn.net/2401_89119815/article/details/147710543  浏览:    关键词:数据结构学习之链表学习:单链表

        在之前顺序表的学习过程中,我们知道顺序表中头插和中插的复杂度是O(N)。那我们可不可以将其降低为O(1)呢?可不可以不增容想用就用想扔就扔而不会浪费一点空间呢?那就是我们今天的内容:链表

        继我们学习了顺序表之后,接下来我们就来学习顺序表的下一个内容:链表。

目录

链表

单链表

        单链表的组成

       创建单链表的新节点

        单链表的尾插

        单链表的头插

       单链表的尾删

        单链表的头删

        单链表的查找

        单链表插入数据

        指定位置之前

        指定位置之后

单链表删除数据 

        指定结点

        指定节点之后

         单链表的结点数据修改

        单链表的销毁


链表

        链表是一种物理存储结构上非连续、非顺序的存储结构,数据元素的逻辑顺序是链表中指针链接次序的实现

        那么链表是如何实现的呢?

        以火车为例,火车的车头和车厢并不是粘连在一起的,而是通过连接起来的,可以增减车厢的个数。每个车厢是独立存在的,增减某一元素对原有数据不影响

        再单链表中“车厢”就是结点,而单链表就是由结点构成的。

        而由于上图可知,结点是由数据元素及保存下一个结点地址的指针组成的。

        接下来我们来学习单链表的实现。

单链表

        我们需要三个文件来准备

SList.h——声明结构体的组成、声明函数方法

SList.c——实现函数

test.c——单链表测试

        单链表的组成

typedef int SListDataType;
//定义链表结构——节点
typedef struct SListNode
{SListDataType data;//保存的数据struct SListNode* next;//指向下一个节点的指针
}SLN;

        单链表的打印函数

SList.h

//链表的打印
void SLN_print(SLN* next);

SList.c

//链表的打印
void SLN_print(SLN* phead)
{SLN* p = phead;while(p!= NULL)//可以简写为while(p){printf("%d ->", p->data);p = p->next;}printf("NULL\n");
}

       创建单链表的新节点

//根据x创建节点
SLN* SLN_buy_node(SListDataType x)
{//根据x创建结点SLN* new_node = (SLN*)malloc(sizeof(SLN));if (new_node == NULL){perror("malloc error");return  1;}new_node->data = x;new_node->next = NULL;return new_node;
}

        单链表的尾插

        前面我们知道链表是依赖于指针连接起来的,所以我们创建一个空链表是这样的

//创建一个空链表
SLN* slist =NULL;

        因此,我们进行尾插的时候分为两种情况:链表为空和链表不为空

        链表为空时,直接将新结点赋值给首结点;不为空时,从前往后走,当指针不为空时向后走,当指针为空的时候跳出循环,插入结点,尾部节点指针为NULL。

        代码为:

//链表的尾插
void SLN_back_push(SLN** pphead, SListDataType x)
{SLN* new_node = SLN_buy_node(x);//链表为空if (*pphead == NULL){*pphead = new_node;}else{//找尾结点SLN* ptail = *pphead;while (ptail->next){ptail = ptail->next;}//找到尾结点ptail->next = new_node;}
}

        单链表的头插

        头插思路:

        

//链表的头插
void SLN_head_push(SLN** pphead, SListDataType x)
{assert(pphead!=NULL);SLN* new_node = SLN_buy_node(x);new_node->next = *pphead;*pphead = new_node;
}

       单链表的尾删

//链表的尾删
void SLN_back_pop(SLN** pphead)
{assert(pphead!= NULL&&*pphead!= NULL);//找prev和ptailSLN*prev = NULL;SLN*ptail = *pphead;//找尾结点while (ptail->next != NULL){prev = ptail;ptail=ptail->next;}prev->next = NULL;free(ptail);ptail = NULL;
}

但是这段代码当链表只有一个结点的时候就会出错, 

        单链表的头删

void SLN_head_pop(SLN** pphead)
{assert(pphead != NULL && *pphead != NULL);SLN*next=(*pphead)->next;free(*pphead);*pphead=next;
}

        单链表的查找

//链表的查找
SLN* SLN_find(SLN* pphead, SListDataType x)
{SLN*p=pphead;while (p != NULL){if (p->data == x){printf("find %d\n", x);return p;}p = p->next;}return NULL;
}

        单链表插入数据

        指定位置之前

        思路图:

        prev的指针指向newcode的数值,newcode的next指针指向pos 

        代码:

//指定位置之前插入
void SLN_Before_insert(SLN** pphead, SLN* pos, SListDataType x) 
{assert(pphead != NULL && pos != NULL);// pos 是头节点,执行头插if (pos == *pphead) {SLN_head_push(pphead, x);}else {SLN* newnode = SLN_buy_node(x);// 找pos的前一个结点SLN * prev = *pphead;while (prev->next != pos){prev = prev->next;}	//prev--> newnode--> pos prev->next = newnode;prev->next = newnode;newnode->next = pos;}
}

        指定位置之后

思路图:

        

        只需要将pos的next指针指向newcode,将nextcode的next指针指向下一个数据,即以下这种情况:

newcode->next=pos->next;
pos->next=newcode;

代码:

//指定位置之后插入
void SLN_After_insert( SLN* pos, SListDataType x)
{assert(pos);SLN* newnode = SLN_buy_node(x);newnode->next = pos->next;pos->next = newnode;	
}

单链表删除数据 

        指定结点

        思路:

        

//指定结点删除
void SLN_delete(SLN** pphead, SLN* pos)
{assert(pphead&&pos);//pos是第一个节点if (pos==*pphead){SLN_head_pop(pphead);}else{SLN* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;}	
}

        指定节点之后

//指定位置之后插入
void SLN_After_insert( SLN* pos, SListDataType x)
{assert(pos);SLN* newnode = SLN_buy_node(x);newnode->next = pos->next;pos->next = newnode;	
}

         单链表的结点数据修改

//链表的数据修改
void SLN_change(SLN** pphead, SLN* pos, SListDataType x)
{assert(pos);pos->data = x;
}

        单链表的销毁

//链表的销毁
void SLN_destroy(SLN** pphead)
{SLN* p = *pphead;while (p != NULL){SLN*next=p->next;free(p);p=next;}*pphead = NULL;
}

       完整代码:

        SList.h

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int SListDataType;
//定义链表结构——节点
typedef struct SListNode
{SListDataType data;//保存的数据struct SListNode* next;//指向下一个节点的指针
}SLN;
//链表的打印
void SLN_print(SLN* next);
//链表的头插
void SLN_head_push(SLN** pphead, SListDataType x);
//链表的尾插
void SLN_back_push(SLN** pphead, SListDataType x);
//链表的尾删
void SLN_back_pop(SLN** pphead);
//链表的头删
void SLN_head_pop(SLN** pphead);
//链表的查找
SLN* SLN_find(SLN* pphead, SListDataType x);
//指定结点删除
void SLN_delete(SLN** pphead, SLN* pos);
//指定结点之后删除
void SLN_After_delete(SLN** pphead, SLN* pos);
//指定位置之前插入
void SLN_Before_insert(SLN** pphead, SLN* pos, SListDataType x);
//指定位置之后插入
void SLN_After_insert(SLN* pos, SListDataType x);
//链表的数据修改
void SLN_change(SLN** pphead, SLN* pos, SListDataType x);
//链表的销毁
void SLN_destroy(SLN** pphead);

        SList.c

#define _CRT_SECURE_NO_WARNINGS
#include"SList.h"
//链表的打印
void SLN_print(SLN* phead)
{SLN* p = phead;while(p!= NULL)//可以简写为while(p){printf("%d ->", p->data);p = p->next;}printf("NULL\n");
}
//根据x创建节点
SLN* SLN_buy_node(SListDataType x)
{//根据x创建结点SLN* new_node = (SLN*)malloc(sizeof(SLN));if (new_node == NULL){perror("malloc error");return  1;}new_node->data = x;new_node->next = NULL;return new_node;
}
//链表的头插
void SLN_head_push(SLN** pphead, SListDataType x)
{assert(pphead!=NULL);SLN* new_node = SLN_buy_node(x);new_node->next = *pphead;*pphead = new_node;
}
//链表的尾插
void SLN_back_push(SLN** pphead, SListDataType x)
{SLN* new_node = SLN_buy_node(x);//链表为空if (*pphead == NULL){*pphead = new_node;}else{//找尾结点SLN* ptail = *pphead;while (ptail->next){ptail = ptail->next;}//找到尾结点ptail->next = new_node;}
}
//链表的尾删
void SLN_back_pop(SLN** pphead)
{assert(pphead!= NULL&&*pphead!= NULL);//当只有一个结点时,直接删除if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else {//找prev和ptailSLN* prev = NULL;SLN* ptail = *pphead;//找尾结点while (ptail->next != NULL){prev = ptail;ptail = ptail->next;}prev->next = NULL;free(ptail);ptail = NULL;}
}
//链表的头删
void SLN_head_pop(SLN** pphead)
{assert(pphead != NULL && *pphead != NULL);SLN*next=(*pphead)->next;free(*pphead);*pphead=next;
}
//链表的查找
SLN* SLN_find(SLN* pphead, SListDataType x)
{SLN*p=pphead;while (p != NULL){if (p->data == x){printf("find %d\n", x);return p;}p = p->next;}return NULL;
}
//指定结点删除
void SLN_delete(SLN** pphead, SLN* pos)
{assert(pphead&&pos);//pos是第一个节点if (pos==*pphead){SLN_head_pop(pphead);}else{SLN* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;}	
}
//指定结点之后删除
void SLN_After_delete(SLN** pphead, SLN* pos)
{assert(pos&&pos->next);SLN*del=pos->next;pos->next=del->next;free(del);del=NULL;
}
//指定位置之前插入
void SLN_Before_insert(SLN** pphead, SLN* pos, SListDataType x) 
{assert(pphead && pos&&*pphead);SLN* newnode = SLN_buy_node(x);//指向第一个节点时if (pos == *pphead){SLN_head_push(*pphead, x);}else{SLN* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = newnode;newnode->next = pos;}
}
//指定位置之后插入
void SLN_After_insert( SLN* pos, SListDataType x)
{assert(pos);SLN* newnode = SLN_buy_node(x);newnode->next = pos->next;pos->next = newnode;	
}
//链表的销毁
void SLN_destroy(SLN** pphead)
{SLN* p = *pphead;while (p != NULL){SLN*next=p->next;free(p);p=next;}*pphead = NULL;
}
//链表的数据修改
void SLN_change(SLN** pphead, SLN* pos, SListDataType x)
{assert(pos);pos->data = x;
}

        test.c

#define _CRT_SECURE_NO_WARNINGS
#include"SList.h"
//测试创建链表和打印
void test1()
{SLN* slist1 = (SLN*)malloc(sizeof(SLN));SLN* slist2 = (SLN*)malloc(sizeof(SLN));SLN* slist3 = (SLN*)malloc(sizeof(SLN));SLN* slist4 = (SLN*)malloc(sizeof(SLN));SLN* slist5 = (SLN*)malloc(sizeof(SLN));slist1->data = 1;slist2->data = 2;slist3->data = 3;slist4->data = 4;slist5->data = 5;slist1->next = slist2;slist2->next = slist3;slist3->next = slist4;slist4->next = slist5;slist5->next = NULL;SLN* slist = slist1;SLN_print(slist);
}
//测试尾插
void test2()
{//创建一个空链表SLN* slist =NULL;SLN_print(slist);SLN_back_push(&slist, 1);SLN_back_push(&slist, 2);SLN_back_push(&slist, 3);SLN_back_push(&slist, 4);SLN_back_push(&slist, 5);SLN_print(slist);
}
//测试头插
void test3()
{//创建一个空链表SLN* slist = NULL;SLN_head_push(&slist,1);SLN_head_push(&slist, 3);SLN_print(slist);
}
//测试尾删除
void test4()
{//创建一个链表SLN* slist = NULL;SLN_print(slist);SLN_back_push(&slist, 1);SLN_back_push(&slist, 2);SLN_back_push(&slist, 3);SLN_back_push(&slist, 4);SLN_back_push(&slist, 5);SLN_print(slist);SLN_back_pop(&slist);SLN_print(slist);SLN_back_pop(&slist);SLN_print(slist);SLN_back_pop(&slist);SLN_print(slist);SLN_back_pop(&slist);SLN_print(slist);SLN_back_pop(&slist);SLN_print(slist);//如果再次使用下面的代码就是访问空指针导致程序崩溃/*SLN_back_pop(&slist);SLN_print(slist);*/
}
//测试头删除
void test5()
{//创建一个链表SLN* slist = NULL;SLN_print(slist);SLN_back_push(&slist, 1);SLN_back_push(&slist, 2);SLN_back_push(&slist, 3);SLN_back_push(&slist, 4);SLN_back_push(&slist, 5);SLN_print(slist);SLN_head_pop(&slist);SLN_print(slist);SLN_head_pop(&slist);SLN_print(slist);
}
//测试查找
void test6()
{//创建一个链表SLN* slist = NULL;SLN_print(slist);SLN_back_push(&slist, 1);SLN_back_push(&slist, 2);SLN_back_push(&slist, 3);SLN_back_push(&slist, 4);SLN_back_push(&slist, 5);SLN_print(slist);SLN* n=SLN_find(slist,3);if (n){printf("found:%d\n", n->data);}else{printf("not found\n");}
}
//测试指定位置之前插入
void test7()
{//创建一个链表SLN* slist = NULL;SLN_print(slist);SLN_back_push(&slist, 1);SLN_back_push(&slist, 2);SLN_back_push(&slist, 3);SLN_back_push(&slist, 4);SLN_back_push(&slist, 5);SLN_print(slist);SLN* N_find = SLN_find(slist, 3);SLN_Before_insert(&slist, N_find, 100);SLN_print(slist);SLN*  O_find = SLN_find(slist, 5);SLN_Before_insert(&slist, O_find, 80);SLN_print(slist);
}
//测试指定位置之后插入
void test8()
{//创建一个链表SLN* slist = NULL;SLN_print(slist);SLN_back_push(&slist, 1);SLN_back_push(&slist, 2);SLN_back_push(&slist, 3);SLN_back_push(&slist, 4);SLN_back_push(&slist, 5);SLN_print(slist);SLN* N_find = SLN_find(slist, 3);SLN_After_insert(N_find,90);SLN_print(slist);
}
//测试指定位置删除
test9()
{//创建一个链表SLN* slist = NULL;SLN_print(slist);SLN_back_push(&slist, 1);SLN_back_push(&slist, 2);SLN_back_push(&slist, 3);SLN_back_push(&slist, 4);SLN_back_push(&slist, 5);SLN_print(slist);SLN* N_find = SLN_find(slist, 3);SLN_delete(&slist, N_find);SLN_print(slist);
}
//测试指定位置之后删除
void test10()
{//创建一个链表SLN* slist = NULL;SLN_print(slist);SLN_back_push(&slist, 1);SLN_back_push(&slist, 2);SLN_back_push(&slist, 3);SLN_back_push(&slist, 4);SLN_back_push(&slist, 5);SLN_print(slist);SLN* N_find = SLN_find(slist, 3);SLN_After_delete(&slist,N_find);SLN_print(slist);
}
//测试修改链表节点值
void test11()
{//创建一个链表SLN* slist = NULL;SLN_print(slist);SLN_back_push(&slist, 1);SLN_back_push(&slist, 2);SLN_back_push(&slist, 3);SLN_back_push(&slist, 4);SLN_back_push(&slist, 5);SLN_print(slist);SLN* N_find = SLN_find(slist, 3);SLN_change(&slist, N_find, 100);SLN_print(slist);
}
//测试销毁链表
void test12()
{//创建一个链表SLN* slist = NULL;SLN_print(slist);SLN_back_push(&slist, 1);SLN_back_push(&slist, 2);SLN_back_push(&slist, 3);SLN_back_push(&slist, 4);SLN_back_push(&slist, 5);SLN_print(slist);SLN_destroy(&slist);
}
int main()
{//test1();//test2();//test3();//test4();//test5();//test6();//test7();//test8();//test9();//test10();//test11();//test12();return 0;
}

        本期单链表的内容到此结束,后续我们会就单链表的问题进行一些题目的练习,或者学习其他的链表结构。在这里求个赞,谢谢

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词