链表:每个节点包含两部分,一部分存放数据变量的data,另一部分是指向下一节点的next指针。
以下来实现不带头节点的单链表。每一个节点是结构体类型的,如下所示:
typedef struct SListNode{
DateType data;//数据域
struct SListNode* next;//指针域
}SListNode;
单链表最基本的功能有:头插,头删,尾插,尾删.......等,在每次头插或者尾插时都需要申请一个新的节点,所以把单独申请节点封装成一个函数,将申请到的节点的地址返回即可,代码如下:
SListNode * BuySListNode(DateType x){//在堆上动态申请一个节点,并返回节点首的地址
SListNode *cur =(SListNode *) malloc(sizeof(SListNode));//申请大小为sizeof(SListNode)的空间
if (cur == NULL){//申请失败,直接退出程序
perror("malloc");
exit(0);
}
cur->data = x;//修改申请的节点的data的值
cur->next = NULL;//将新申请的节点的next置为空
return cur;
}
首先会定义一个结构体指针,让它先指向空,然后通过调用不同的函数来实现不同的功能。
SListNode *plist=NULL;
1.头插:
void SListPushFront(SListNode ** plist, DateType x){//头插
assert(plist);//判断指针的合法性
SListNode *cur = *plist;//赋值后,cur此时相当于头指针
*plist = BuySListNode(x);//改变头指针的指向,使其指向新申请的节点
(*plist)->next = cur;//让新申请的节点的next指向原来的链表头
}
为什么要使用二级指针?因为在这里的每一次头插都需要改变指针plist的指向,而且头插的这个函数没有返回值,所以只有对函数中的二级指针解引用才能拿到真正的函数头指针,从而对它进行操作,改变它的指向。如果不想使用二级指针,那么函数必须要有返回值,而且返回值为:SListNode *类型。
2.尾插:
void SListPushBack(SListNode ** plist, DateType x){//尾插
if (*plist == NULL){//链表中没有节点,此时直接插入
*plist = BuySListNode(x);
}
else{
SListNode *cur = *plist;
while (cur->next){//cur的next为空,说明找到了最后一个节点,为尾插做准备
cur = cur->next;
}
cur->next = BuySListNode(x);//插入节点
}
}
尾插也需要使用二级指针,因为如果链表中没有节点那么此时还是需要改变定义出来的头指针的指向,这是一种特殊情况。如果要插入的链表中的节点个数>=1,那么此时可以不使用二级指针,因为一定可以找到最后一个节点的next域。
3.头删:每次需要改变头指针的指向,所以必须使用二级指针。
4.尾删:当链表只有一个节点的时候,这也属于一种特殊情况,因为此时要让链表的头指针指向空,所以要使用二级指针。如果要删除的链表中的节点个数>1,此时可以不使用二级指针,因为不需要改变头指针的指向。
搞清楚了二级指针在什么情况下使用,那么写出单链表的增、删、改、查就容易了,以下是完整代码和测试用例,使用多文件:
1.SListNode.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <windows.h>
#include <assert.h>
typedef int DateType;
typedef struct SListNode{
DateType data;
struct SListNode* next;
}SListNode;
extern SListNode * BuySListNode(DateType x);
extern void SListPushBack(SListNode ** plist, DateType x);
extern void SListPushFront(SListNode ** plist, DateType x);
extern void SListPopFront(SListNode **plist);
extern void SListPopBack(SListNode **plist);
extern void SListInsertAfter(SListNode* pos, DateType x);
extern void SListPrint(SListNode *plist);
2.SListNode.c
#include "SListNode.h"
SListNode * BuySListNode(DateType x){//在堆上动态申请一个节点,并返回节点首的地址
SListNode *cur =(SListNode *) malloc(sizeof(SListNode));//申请大小为sizeof(SListNode)的空间
if (cur == NULL){//申请失败,直接退出程序
perror("malloc");
exit(0);
}
cur->data = x;//修改申请的节点的data的值
cur->next = NULL;//将新申请的节点的next置为空
return cur;
}
void SListPushFront(SListNode ** plist, DateType x){//头插
assert(plist);//判断指针的合法性
SListNode *cur = *plist;//赋值后,cur此时相当于头指针
*plist = BuySListNode(x);//改变头指针的指向,使其指向新申请的节点
(*plist)->next = cur;//让新申请的节点的next指向原来的链表头
}
void SListPopFront(SListNode **plist){//头删
if (*plist == NULL){//链表中没有节点,不用删除
return;
}
else{
SListNode *cur = *plist;//赋值后,cur此时相当于头指针
*plist = (*plist)->next;//让头指针指向原来链表中的第二个节点
free(cur);//释放要删除的节点所占用的空间
}
}
void SListPushBack(SListNode ** plist, DateType x){//尾插
assert(plist);
if (*plist == NULL){//链表中没有节点,此时直接插入
*plist = BuySListNode(x);
}
else{
SListNode *cur = *plist;
while (cur->next){//cur的next为空,说明找到了最后一个节点,为尾插做准备
cur = cur->next;
}
cur->next = BuySListNode(x);//插入节点
}
}
void SListPopBack(SListNode **plist){//尾删
if (*plist == NULL){//没有节点,直接返回
return;
}
else if (((*plist)->next)==NULL){//只有一个节点,此时可以直接删除
free(*plist);
*plist = NULL;
}
else{
SListNode *cur = *plist;//赋值后,cur此时相当于头指针
SListNode *prev = cur;//赋值后,prev此时相当于头指针
while (cur->next){//寻找最后一个节点,cur指向最后一个节点,而prev指向最后一个节点的前一个节点
prev = cur;
cur = cur->next;
}
prev->next = NULL;//把倒数第二个节点的next置空
free(cur);//释放最后一个节点所占用的空间
}
}
void SListPrint(SListNode *plist){//打印
if (plist == NULL){
printf("没有元素\n");
return;
}
SListNode * cur = plist;
while(cur){//遍历单链表,直到cur为空
printf("->%d", cur->data);
cur = cur->next;
}
printf("\n");
}
SListNode* SListFind(SListNode* plist, DateType x){//查找
if (plist == NULL){
printf("没有元素\n");
return NULL;
}
SListNode *cur = plist;
while (cur!=NULL){
if (cur->data == x){
printf("找到了\n");
return cur;
}
cur = cur->next;
}
return NULL;
}
void SListInsertAfter(SListNode* pos, DateType x){//在pos位置之后插入
if (pos == NULL){
return;
}
SListNode *cur = BuySListNode(x);
cur->next = pos->next;
pos->next = cur;
/*SListNode *cur = pos->next;
pos->next = BuySListNode(x);
pos->next->next = cur;*/
}
void SListEraseAfter(SListNode* pos){//删除pos位置之后的节点
if (pos == NULL||(pos->next)==NULL){
return;
}
SListNode *cur = pos->next;
pos->next = cur->next;
free(cur);
}
3.main.c
#include "SListNode.h"
int main(){
SListNode *plist=NULL;
SListPushFront(&plist, 7);
SListPushFront(&plist, 9);
SListPrint(plist);
SListPushBack(&plist, 5);
SListPushBack(&plist, 4);
SListPrint(plist);
SListPopFront(&plist);
SListPrint(plist);
SListPopBack(&plist);
SListPrint(plist);
SListInsertAfter(plist,85);
SListPrint(plist);
system("pause");
return 0;
}
运行程序后,结果如下: