概述:
将非连续地址空间里的数据以一种特殊的方式(指针) 串在一起。
带头结点的单链表结构:
每个节点有两个域,数据域存储数据,指针域存储下一个节点的地址,无后续节点,则指针域为NULL(空指针)。
如图:
结构设计:
#include <stdio.h>
#include <stdlib.h>
#include <assetr.h>
typedef int ElemType;
typedef struct ListNode
{
ElemType data; //存储数据
struct ListNode* next; //指针域
}ListNode,*PListNode; //节点类型
typedef struct
{
ListNode* head; // 头节点
int cursize; // 数据个数;
}LinkList; //链表头节点
1.动态申请节点函数:
利用malloc函数申请内存空间,进行数据写入
ListNode* Buynode(ElemType val,ListNode *narg) //数据和下一节点地址
{
ListNode* s = (ListNode*)malloc(sizeof(ListNode));
if (nullptr == s)
{
exit(EXIT_FAILURE); //内存申请失败,终止程序
}
s->data = val; //写入数据
s->next = narg; //next指针指向下一节点
return s;
}
2.初始化函数(创建一个空链表)
void InitList(LinkList* plist)
{
assert(plist != nullptr); //安全性检测
plist->cursize = 0; //数据个数
plist->head = Buynode(0, nullptr);
}
3.释放节点函数
void Freenode(ListNode* p)
{
free(p); //函数内部有空指针检测,无需assert检测
p=nullptr; //置空
}
4.在指定节点后插入节点 函数
需要先将插入节点的指针指向后续节点,然后将上一个节点的指针指向插入节点便可。
(先1后2,顺序不可改变,否则将无法找到后续节点)
void InsertNext(LinkList* plist, ListNode* ptr, ElemType val)
{
assert(plist != nullptr && ptr != nullptr);
ptr->next = Buynode(val, ptr->next);
plist->cursize += 1;
}
尾插法:
void InsertTail(LinkList* plist, ElemType val)
{
assert(plist != nullptr);
ListNode* p = plist->head;
while (p->next != nullptr) //找到最后一个节点
{
p = p->next;
}
InsertNext(plist, p, val); //代码复用
}
头插法:
void InsertHead(LinkList* plist, ElemType val)
{
assert(plist != nullptr);
InsertNext(plist,plist->head,val); //代码复用
}
5.删除指定节点的后一节点
用指针保存待删节点的地址,将指定节点的指针指向待删节点的后一节点,释放待删节点的内存。
void Erasenext(LinkList* plist,ListNode* ptr)
{
assert(plist != nullptr && ptr != nullptr);
if(ptr->next == nullptr) return ;
ListNode* p = ptr->next;
ptr->next = p->next;
Freenode(p);
plist->cursize--;
}
头删法:
void EraseFrist(LinkList* plist)
{
assert(plist != nullptr);
Erasenext(plist,plist->head);
}
尾删法:
void EraseLast(LinkList* plist)
{
assert(plist != nullptr);
if(plist->cursize == 0) return ;
ListNode* ptr,*str;
ptr=plist->head->next;
str=plist->head;
while(ptr != nullptr && ptr->next != nullptr)
{
str= ptr;
ptr = ptr->next;
}
Erasenext(plist,str);
}
6.查找指定元素的前驱节点:
原理:双指针遍历法
ListNode* Findvalueprev(LinkList* plist,ElemType val)
{
assert(plist != nullptr);
ListNode* ptr=plist->head->next;
ListNode* ptrprev=plist->head;
while(ptr->data != val && ptr != nullptr)
{
ptrprev=ptr;
ptr=ptr->next;
}
if(ptr == nullptr ) ptrprev = nullptr;
return ptrprev;
}
查找指定元素的节点:
ListNode* Findvalue(LinkList* plist,ElemType val)
{
assert(plist != nullptr);
ListNode* str=Findvalueprev(plist,val);
if(str != nullptr) {
str = str->next;
}
return str;
}
7.删除指定元素的节点:
void remove(LinkList* plist,ElemType val)
{
assert(plist != nullptr);
Erasenext(plist,Findvalueprev(plist,val));
}
删除指定元素的所有节点:
void removeall(LinkList* plist,ElemType val)
{
assert(plist != nullptr);
ListNode* p = plist->head;
for(;p->next != nullptr;)
{
if(p->next->data == val) {
Erasenext(plist,p);
}else{
p=p->next;
}
}
}
8.清空链表:
void clearList(LinkList* plist)
{
assert(plist != nullptr);
while(plist->cursize){
EraseFrist(plist);
}
}
9.销毁链表:
void DestroyList(LinkList *plist)
{
assert(plist != nullptr);
clearList(plist);
Freenode(plist->head);
}
10.打印链表:
void PrintList(LinkList* plist)
{
assert(plist != nullptr);
ListNode* p = plist->head->next; // first;
while (p != nullptr)
{
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}