前言
下面我会来介绍链表的建立,头删,尾删,头插,尾插,查找,查找删除又分为不同的方法
一、链表的预备
我们将在头文件中建立结点,以及对数据类型进行定义,方便后续的修改
1.1链表类型的定义
typedef int SLTDateType;//SLT为链表的简写与SL顺序表区分
//可以根据自己的需要修改类型
1. 2链表的建立
typedef struct SListNode {
SLTDateType date;//存储的数据
struct SListNode* next;//用于存储下一个结点的位置
}SLTNode;
二、链表的基本操作
2.1链表的打印(SLTPrint)
void SLTPrint(SLTNode* phead) {//打印
//链表为空照样可以打印所以不需要
//assert检查是否为空
SListNode* cur = phead;
while (cur) {
printf("%d->", cur->date);
cur = cur->next;
//指向下一个结点
}
printf("NULL\n");
}
2.2新节点的建立(BuySLTNode)
SLTNode* BuySLTNode(SLTDateType x) {//建立一个新的结点
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));
if (newnode == NULL) {//判断新节点是否为空
perror("BuySLTNode");
return NULL;
}
//不为空对新结点进行初始化
newnode->next = NULL;
newnode->date = x;
return newnode;//返回新的结点
}
2.3链表的尾插(SLTPushBack)
void SLTPushBack(SLTNode** pphead,SLTDateType x) {//尾插
assert(pphead);
//这里之所以传二级指针是因为我们需要对头指针指向的位置进行修改
//举个例子:我们修改int的变量要传int*类型
//那么我们要修改int*类型就要传int**类型
//头指针类型为SLTNode*类型我们要修改就要传
//SLTNode**类型才能对其内容进行修改
SLTNode* newnode = BuySLTNode(x);
//插入前先建立一个新节点
if (*pphead == NULL) {
*pphead = newnode;
//链表本身就为空,直接让他指向新结点
}
else {
SLTNode* tail = *pphead;
while (tail->next != NULL) {
//寻找链表的尾结点
tail = tail->next;
}
tail->next = newnode;
//找到尾结点以后让其指针域指向新的结点
}
}
2.4链表的头插(SLTPushFront)
void SLTPushFront(SLTNode** pphead, SLTDateType x) {//头插
//头插比较简单只需要把新结点接上改变头指针的指向就行了
assert(pphead);
SLTNode* newnode = BuySLTNode(x);//建立新结点
newnode->next = *pphead ;
//让新结点的指针域指向*pphead所指向的位置
*pphead = newnode;//头指针指向新结点
//无论原先链表为不为空都适用
}
2.5链表的尾删(SLTPopBack)
void SLTPopBack2(SLTNode** pphead) {//尾删
assert(pphead);
assert(*pphead);//肯定要有结点才能删,所以要判断是否为空
if ((*pphead)->next == NULL) {//只有一个结点的情况
free(*pphead);//直接把结点释放掉置空就完事了
*pphead = NULL;
}
else {//两个及两个结点以上的情况
SLTNode* prev = NULL;//用来存tail前一个结点
SLTNode* tail = *pphead;//用来找尾结点
while (tail->next != NULL) {
prev = tail;
tail = tail->next;
}
free(tail);//找到尾结点后释放
prev->next = NULL;
// 让尾结点前一个元素的指针域置为空,因为你后面一个结点释放掉了
//没有必要让prev存一个没有用的地址
}
}
2.6链表的头删(SLTPopFront)
void SLTPopFront(SLTNode** pphead ) {//头删
assert(pphead);
assert(*pphead);//肯定要有结点才能删,所以要判断是否为空
SLTNode* first = *pphead;//保存头指针当前指向结点
*pphead = first->next;//让头指针指向当前结点的下一个指针域
free(first);
first = NULL;
}
2.7链表的插入1(在指定结点之前的插入)(SLTInsertBefore)
void SLTInsertBefore(SLTNode** pphead, SLTNode* pos, SLTDateType x) {//在pos之前插入元素
assert(pos);//判断结点是否有意义
assert(pphead);
if (*pphead == pos) {//在pos前插入此时pos为头结点
SLTNode* newnode = BuySLTNode(x);
newnode->next = *pphead;
*pphead = newnode;
//等价于头插
//SLTPushFront(pphead,x);
}
else {//pos不为头结点
SLTNode * prev=*pphead;
while (prev->next != pos) {
prev = prev->next;
//寻找pos之前的一个结点
}
SLTNode* newnode = BuySLTNode(x);
newnode->next =pos;
//新结点后面接上pos
prev->next = newnode;
//原先pos之前的结点再接上新结点
}
}
2.75链表的插入2(在指定结点之后插入)(SLTInsertAfter)
void SLTInsertAfter( SLTNode*pos, SLTDateType x) {//从pos后面插入,常用
//不需要遍历链表也能插入
assert(pos);
SLTNode* newnode = BuySLTNode(x);
newnode->next = pos->next;
//新结点的指针域指向pos的指针域,相当于新结点接上
//pos后面的元素
pos->next = newnode;//pos接上新结点
}
2.8 链表的删除(删除指定的结点)(SLTErase)
void SLTErase (SLTNode** pphead, SLTNode* pos) {//删除指定结点pos
assert(pphead);
assert(pos);//判断结点是否有意义
if (*pphead == pos) {//要删除元素为头结点
SLTNode* first = *pphead;//保存当前头结点
*pphead = first->next;//头指针指向下一个位置
free(first);//释放
first = NULL;
//SLTPopFront
//相当于头删
}
else {
SLTNode* prev = *pphead;
while (prev->next != pos) {
prev = prev->next;
}
//寻找pos之前的一个结点
prev->next = pos->next;
//prev指针域存pos后面的结点
free(pos);
pos = NULL;
}
}
2.85链表的删除(删除指定结点后面一个结点)(SLTEraseAfter)
void SLTEraseAfter( SLTNode* pos) {//pos后面一个结点的删除,常用
assert(pos);
assert(pos->next);//判断pos后面是否又结点
//不需要遍历链表也能插入
SLTNode* del = pos->next;//要删除结点
pos->next = del->next;
//pos的指针域存要删除结点的下一个结点
free(del);
del = NULL;
}
2.9链表的删除(SLTDestroy)
void SLTDestroy(SLTNode** pphead) {
assert(pphead);
SLTNode* cur = *pphead;
while (cur) {
SLTNode*tmp = cur->next;//保存当前节点的下一个结点
free(cur);
cur = tmp;//让当前结点指向下一个结点
}
*pphead = NULL;
}