> 技术文档 > 数据结构(3)单链表

数据结构(3)单链表


1、链表的引入

        顺序表带来了很多方便,但是又带来了以下几个问题:(1)中间/头部的插入和删除,时间复杂度为O(N) ;(2)增容需要申请新空间,拷贝数据,释放旧空间,会有不小的消耗 ; (3)增容一般是呈二倍增长,势必会有一定的空间浪费 。

        想要解决以上的问题,就需要一个新的数据结构——链表

2、概念与结构

        链表是一种物理存储结构上非连续、非顺序的存储结构。链表由结点组成,结点的组成主要有两个部分:当前结点要保存的数据 和 保存下一个结点的地址(指针变量)。

struct SListNode{//存储的数据int data;//指向下一个节点的指针struct SListNode* next;};
SLTNode* SLTbuyNode(SLTDataType x){//根据x创建结点SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));if (newnode == NULL){perror(\"malloc fail!\");exit(1);}newnode->data = x;newnode->next = NULL;return newnode;}

 

3、单链表的实现

 

//尾插void SLTPushBack(SLTNode** pphead, SLTDataType x){SLTNode* newnode = SLTbuyNode(x);//链表为空if (*pphead == NULL){*pphead = newnode;}else{//找尾SLTNode* ptail = *pphead;while (ptail->next){ptail = ptail->next;}ptail->next = newnode;}}

//头插void SLTPushFront(SLTNode** pphead, SLTDataType x){assert(pphead);SLTNode* newnode = SLTbuyNode(x);newnode->next = *pphead;*pphead = newnode;}

//尾删void SLTPopBack(SLTNode** pphead){assert(pphead && *pphead);//只有一个结点if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else{//找prev和ptailSLTNode* prev = NULL;SLTNode* ptail = *pphead;while (ptail->next){prev = ptail;ptail = ptail->next;}prev->next = NULL;free(ptail);ptail = NULL;}}

//头删void SLTPopFront(SLTNode** pphead){assert(pphead && *pphead);SLTNode* next = (*pphead)->next;free(*pphead);*pphead = next;}

首先要找到pos位置的前一个节点prev,遍历整个链表,循环条件prev->next != pos。如果pos位置刚好是头结点,循环会出现空指针异常,因此要分两种情况。

//在指定位置之前插入数据void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x){assert(pphead && pos);//当pos指向第一个节点,是头插if (pos = *pphead){SLTPushFront(pphead, x);}else{SLTNode* newnode = SLTbuyNode(x);//找pos的前一个节点SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = newnode;newnode->next = pos;}}

 

//在指定位置之后插入数据void SLTInsertAfter(SLTNode* pos, SLTDataType x){assert(pos);SLTNode* newnode = SLTbuyNode(x);newnode->next = pos->next;pos->next = newnode;}

 这里为什么函数参数部分只有两个参数?因为我们可以直接通过pos->next来找到下一个节点,就不用像前面那样传头结点的指针来遍历链表。

//删除pos结点void SLTErase(SLTNode** pphead, SLTNode* pos){assert(pphead && pos);//pos是头结点if (pos == *pphead){SLTPopFront(pphead);}else{SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;}}

4、完整参考代码

SList.h
#pragma once#include #include #include //定义链表的结构——结点的结构typedef int SLTDataType;typedef struct SListNode{SLTDataType data; //存储的数据struct SListNode* next; //指向下一个节点}SLTNode;//链表的打印void SLTPrint(SLTNode* phead);//尾插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);//在指定位置之前插入数据void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x);//在指定位置之后插入数据void SLTInsertAfter(SLTNode* pos, SLTDataType x);//删除pos结点void SLTErase(SLTNode** pphead, SLTNode* pos);//删除pos位置之后的结点void SLTEraseAfter(SLTNode* pos);//销毁链表void SListDestroy(SLTNode** pphead);
SList.c
#define _CRT_SECURE_NO_WARNINGS 1#include \"SList.h\"//链表的打印void SLTPrint(SLTNode* phead){SLTNode* pcur = phead;while (pcur){printf(\"%d -> \", pcur->data);pcur = pcur->next;}printf(\"NULL\\n\");}SLTNode* SLTbuyNode(SLTDataType x){//根据x创建结点SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));if (newnode == NULL){perror(\"malloc fail!\");exit(1);}newnode->data = x;newnode->next = NULL;return newnode;}//尾插void SLTPushBack(SLTNode** pphead, SLTDataType x){SLTNode* newnode = SLTbuyNode(x);//链表为空if (*pphead == NULL){*pphead = newnode;}else{//找尾SLTNode* ptail = *pphead;while (ptail->next){ptail = ptail->next;}ptail->next = newnode;}}//头插void SLTPushFront(SLTNode** pphead, SLTDataType x){assert(pphead);SLTNode* newnode = SLTbuyNode(x);newnode->next = *pphead;*pphead = newnode;}//尾删void SLTPopBack(SLTNode** pphead){assert(pphead && *pphead);//只有一个结点if ((*pphead)->next == NULL){free(*pphead);*pphead = NULL;}else{//找prev和ptailSLTNode* prev = NULL;SLTNode* ptail = *pphead;while (ptail->next){prev = ptail;ptail = ptail->next;}prev->next = NULL;free(ptail);ptail = NULL;}}//头删void SLTPopFront(SLTNode** pphead){assert(pphead && *pphead);SLTNode* next = (*pphead)->next;free(*pphead);*pphead = next;}//查找SLTNode* SLTFind(SLTNode* phead, SLTDataType x){SLTNode* pcur = phead;while (pcur){if (pcur->data == x){return pcur;}pcur = pcur->next;}return NULL;}//在指定位置之前插入数据void SLTInsert(SLTNode** pphead, SLTNode* pos, SLTDataType x){assert(pphead && pos);//当pos指向第一个节点,是头插if (pos = *pphead){SLTPushFront(pphead, x);}else{SLTNode* newnode = SLTbuyNode(x);//找pos的前一个节点SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = newnode;newnode->next = pos;}}//在指定位置之后插入数据void SLTInsertAfter(SLTNode* pos, SLTDataType x){assert(pos);SLTNode* newnode = SLTbuyNode(x);newnode->next = pos->next;pos->next = newnode;}//删除pos结点void SLTErase(SLTNode** pphead, SLTNode* pos){assert(pphead && pos);//pos是头结点if (pos == *pphead){SLTPopFront(pphead);}else{SLTNode* prev = *pphead;while (prev->next != pos){prev = prev->next;}prev->next = pos->next;free(pos);pos = NULL;}}//删除pos位置之后的结点void SLTEraseAfter(SLTNode* pos){assert(pos && pos->next);SLTNode* del = pos->next;pos->next = del->next;free(del);del = NULL;}//销毁链表void SListDestroy(SLTNode** pphead){SLTNode* pcur = *pphead;while (pcur){SLTNode* next = pcur->next;free(pcur);pcur = next;}*pphead = NULL;}
test.c
#define _CRT_SECURE_NO_WARNINGS 1#include \"SList.h\"void test01(){//创建空链表SLTNode* plist = NULL;//SLTPrint(plist);SLTPushBack(&plist, 1);SLTPushBack(&plist, 2);SLTPushBack(&plist, 3);SLTPushBack(&plist, 4);SLTPrint(plist);//SLTPopBack(&plist);//SLTPrint(plist);//SLTPopFront(&plist);//SLTPrint(plist);SLTNode* find = SLTFind(plist, 3);SLTInsertAfter(find, 100);SLTPrint(plist);SListDestroy(&plist);}int main(){test01();return 0;}

Web前端