SList.h结构体函数声明文件
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLDateType;
//单链表 一个表中有两个属性,值和下一个链表的地址
typedef struct SListNode
{
SLDateType data;
struct SLDateType* next;//类型为结构体目的是为下一个链表储存地址
}SLTNode;
void SListPrint(SLTNode* phead);//打印单链表
void SlistPushBack(SLTNode** phead, SLDateType x);//尾插
void SlistPushFront(SLTNode** phead, SLDateType x);//头插
SLTNode* BuyListNode(SLDateType x);//创建新的空间,存储新的链表
void SListPopBack(SLTNode** pphead);//尾删
void SListPopFront(SLTNode** pphead);//头删
SLTNode* SListFind(SLTNode* phead, SLDateType x);//查找,返回地址
void SListInsert(SLTNode** pphead, SLTNode* pos, SLDateType x);//在pos前面插入
void SListInsertAfter(SLTNode* pos, SLDateType x);//在pos之后插入
SLTNode* removeElements(SLTNode* head, SLDateType x);//指定删除一样的元素
void SListErase(SLTNode** pphead, SLTNode* pos);//指定位置删除
void SListEraseAfter(SLTNode* pos);//删除指定位置的后面一个
void SLisDestory(SLTNode** pphead);//摧毁链表
SList.c函数实现文件
#include"SList.h"
void SListPrint(SLTNode* phead)//打印单链表,传入首节点的地址,只是打印不用二级指针
{
SLTNode* cur = phead; //把第一个链表的地址传给首指针 cur代表了第一个节点的地址
while (cur != NULL)
{
printf("%d->", cur->data);
cur = cur->next; //next存储的是下一个节点的地址
}
printf("NULL\n");
}
SLTNode* BuyListNode(SLDateType x)//创建新的空间存储链表
{
SLTNode* newnode = (SLTNode*)malloc(sizeof(SLTNode));//用malloc函数开辟一片空间在堆上,不在栈上,返回一个地址,
newnode->data = x;
newnode->next = NULL; //以NULL作为结束的标志
if (newnode == NULL)
{
printf("malloc fail\n");
exit(-1);
}
return newnode;
}
/*先前理解 首先,你创建的是结构体指针,你要改变结构体指针,就要把结构体指针的地址传进来,所以要用二级指针
其次你要为结构体里面的指针创建空间,本题为结构体指针,所以先判断,先给结构体一个空间,然后判断如果
结构体有空间,就给结构体里面的结构指针空间,依次递归*/
/*2023.1.12理解 关于尾删可以传一级指针,但是我们要操作头节点时还要传二级指针,所以尾删还要传二级指针的理解。
传二级指针是因为首先我们用malloc创建了一片空间(一个结构体的地址),
然后呢我们要把malloc创建的空间给我们创建的结构体指针,这时我们要区分一个东西,
整个结构体的地址,和结构体里面成员的地址,首先我们让头节点结构体指针置为NULL,
然后要改变头节点结构体地址,
所以要传二级指针,二级指针存储的是头节点结构体地址,然后我们要尾删时要注意,
我们创建的结构体里面包含下一个结构体的地址,
所以要改变下一个结构体的地址,我们可以用一级指针来改变*/
void SlistPushBack(SLTNode** pphead, SLDateType x) //因为要改变传入指针的值,所以要用二级指针,pphead是局部变量
{
SLTNode* newnode = BuyListNode(x);
if (*pphead == NULL) //*pphead代表 SLTNode* pphead 为一个地址。首先要给传入的指针一个地址
{
*pphead = newnode;
}
else //代表已经初始化过传入的指针
{
SLTNode* tail = *pphead;
while(tail->next!=NULL)
{
tail = tail->next; //把结构体中的下一个链表的地址给tail,找到最后一个链表
}
tail->next = newnode; //把新创建的链表链接
}
}
void SlistPushFront(SLTNode** phead, SLDateType x)//头插
{
SLTNode* newnode = BuyListNode(x);
newnode->next = *phead;
*phead = newnode;
}
void SListPopBack(SLTNode** pphead)//尾删
{
//温柔的
if (*pphead == NULL)//先判断链表为空
{
return;
}
//粗暴的
//assert(*pphead != NULL);
if ((*pphead)->next == NULL)//链表只有一个时,按照普通的删会有野指针
{
free(*pphead);
*pphead = NULL;
}
else//链表有2个及2个以上时
{
SLTNode* prev = NULL;
SLTNode* tail = *pphead;//把传入的头指针所储存的第一个链表的地址给tail存储
while (tail->next)
{
prev = tail;
tail = tail->next;
}
free(tail);
tail = NULL;
prev->next = NULL;
}
}
void SListPopFront(SLTNode** pphead)//头删
{
if (*pphead == NULL)//判断链表是否为空
{
return;
}
SLTNode* next = (*pphead)->next;//不用判断链表只有一个,因为如果只有一个的话 (*pphead)->next 为NULL next=NULL
free(*pphead);
*pphead=next;
}
SLTNode* SListFind(SLTNode* phead, SLDateType x)
{
SLTNode* cur = phead;
while (cur)
{
if (cur->data == x)
{
return cur;
}
else
{
cur = cur->next;
}
}
return NULL;
}
void SListInsert(SLTNode** pphead, SLTNode* pos, SLDateType x)//插入
{
SLTNode* newnode = BuyListNode(x);
if(*pphead==pos)
{
newnode->next = *pphead;
*pphead = newnode;
}
else
{
SLTNode* posPrev = *pphead;
while (posPrev->next != pos)
{
posPrev = posPrev->next;
}
posPrev->next = newnode;
newnode->next = pos;
}
}
void SListInsertAfter(SLTNode* pos, SLDateType x)//在pos之后插入
{
SLTNode* newnode = BuyListNode(x);
SLTNode* posAfter = pos->next;
pos->next = newnode;
newnode->next = posAfter;
}
SLTNode* removeElements(SLTNode** head, SLDateType x)//oj题
{
SLTNode* prev = NULL;
SLTNode* cur = *head;
while (cur)
{
if (cur->data==x)
{
//1.头删
//2.中间删
if (cur == *head)
{
*head = cur->next;
free(cur);
cur = *head;
}
else
{
prev->next = cur->next;
free(cur);
cur = prev->next;
}
}
else
{
prev = cur;
cur = cur->next;
}
}
return head;
}
void SListErase(SLTNode** pphead, SLTNode* pos)//删除指定元素
{
if (*pphead == pos)
{
*pphead = pos->next;
free(pos);
}
else
{
SLTNode* prev = *pphead;
while (prev->next != pos)
{
prev = prev->next;
}
prev->next = pos->next;
free(pos);
pos = NULL;
}
}
void SListEraseAfter(SLTNode* pos)//删除指定元素的后一个
{
SLTNode* next = pos->next;
pos->next = next->next;
free(next);
}
void SLisDestory(SLTNode** pphead)
{
SLTNode* cur = *pphead;
while (cur)
{
SLTNode* next = cur->next;
free(cur);
cur = next;
}
*pphead = NULL;
}
test.c测试函数文件
#include"SList.h"
void test1()
{
SLTNode* plist = NULL;
SlistPushBack(&plist, 1);
SlistPushBack(&plist, 2);
SlistPushBack(&plist, 3);
SlistPushBack(&plist, 4);
SListPrint(plist);
SlistPushFront(&plist, 1);
SlistPushFront(&plist, 2);
SlistPushFront(&plist, 3);
SlistPushFront(&plist, 4);
SListPrint(plist);
SListPopBack(&plist);
SListPopBack(&plist);
SListPopBack(&plist);
SListPopBack(&plist);
SListPrint(plist);
}
void test2()
{
SLTNode* plist = NULL;
SlistPushBack(&plist, 1);
SlistPushBack(&plist, 2);
SlistPushBack(&plist, 3);
SlistPushBack(&plist, 2);
SlistPushBack(&plist, 4);
SListPrint(plist);
//多值查找,还可以修改
SLTNode* pos=SListFind(plist,2);
int i = 1;
while (pos)
{
printf("第%d个pos节点:%p->%d\n",i++,pos,pos->data);
pos = SListFind(pos->next, 2);
}
//修改
pos = SListFind(plist, 3);
if (pos)
{
pos->data = 30;
}
SListPrint(plist);
}
void test3()
{
SLTNode* plist = NULL;
SlistPushBack(&plist, 1);
SlistPushBack(&plist, 2);
SlistPushBack(&plist, 3);
SlistPushBack(&plist, 4);
SlistPushBack(&plist, 5);
SListPrint(plist);
SLTNode* pos = SListFind(plist, 3);//在3的前面插入
if (pos)
{
SListInsert(&plist, pos, 30);
}
}
void test4()
{
SLTNode* plist = NULL;
SlistPushBack(&plist, 1);
SlistPushBack(&plist, 2);
SlistPushBack(&plist, 3);
SlistPushBack(&plist, 4);
SlistPushBack(&plist, 5);
SListPrint(plist);
SLTNode* pos = SListFind(plist, 5);
if (pos)
{
SListInsertAfter(pos, 30);
}
SListPrint(plist);
}
void test5()
{
SLTNode* plist = NULL;
SlistPushBack(&plist, 2);
SlistPushBack(&plist, 5);
SlistPushBack(&plist, 2);
SlistPushBack(&plist, 3);
SlistPushBack(&plist, 5);
SlistPushBack(&plist, 4);
SlistPushBack(&plist, 5);
SListPrint(plist);
removeElements(&plist, 2);
SListPrint(plist);
}
void test6()
{
SLTNode* plist = NULL;
SlistPushBack(&plist, 1);
SlistPushBack(&plist, 2);
SlistPushBack(&plist, 3);
SlistPushBack(&plist, 4);
SlistPushBack(&plist, 5);
SListPrint(plist);
SLisDestory(&plist);
SListPrint(plist);
}
int main()
{
//test1();
//test2();
//test3();
//test4();
test5();
//test6();
return 0;
}
总结:本代码为没有哨兵位的单链表所以要传二级指针,只要遇到要操作第一个节点都要传二级指针,还要注意在增删情况下,大部分要考虑链表为空,链表只有一个节点的情况,在学习链表的时候,要多画图。