单链表是一种链式存取的数据结构,,其中数据以节点来表示,每个节点由一个数据元素和一个指向后继元素存储位置的指针构成,在这里,我通过3个文件来编写单链表:list.h list.c test.c。
List.h
list.h中主要是一些函数声明,在这里就不做介绍了,直接粘代码:
#define _CRT_SECURE_NO_WARNINGS 1
#ifndef __LIST_H__
#define __LIST_H__
#include <stdio.h>
#include <assert.h>
#include <malloc.h>
typedef int DataType;
//将int类型重命名,方便修改单链表的类型
typedef struct ListNode
{
DataType data; //元素
struct ListNode* next;//下个位置指针
}ListNode;
ListNode* BuyNode(DataType x);//创建单链表
void PrintList(ListNode* pList);//输出单链表
void PushBack(ListNode** ppList, DataType x);//尾插
void PopBack(ListNode** ppList); //尾删
void PushFront(ListNode** ppList, DataType x); //头插
void PopFront(ListNode** ppList); //头删
ListNode* Find(ListNode* pList, DataType x);//查找
void Insert(ListNode** ppList, ListNode* pos, DataType x);
// 在pos的前面插入一个节点x
void Erase(ListNode** ppList, ListNode* pos);//删除指定节点
#endif
list.c
首先,我们来实现BuyNode函数(创建单链表),函数原型为ListNode* BuyNode(DataType x),传入一个DataType类型变量x,返回一个ListNode类型的指针,我们直接用malloc开辟一个空间,将x赋给Data,next置为空,再返回我们开辟的空间的指针就好了。代码如下:
ListNode* BuyNode(DataType x)
{
ListNode *pList=(ListNode*)malloc(sizeof(ListNode));
pList->data=x;
pList->next=NULL;
return pList;
}
接下来实现PrintList函数(输出单链表),PrintList函数需要输入一个链表的指针,要输出所有的节点就要用到一个while循环,每次循环直接输出data,然后把指针置为next。
代码如下:
void PrintList(ListNode* pList)
{
while(pList)
{
printf("%d->",pList->data);
pList=pList->next;
}
printf("%s\n",pList);//此时pList为空,会输出(null)
}
接下来实现PushBack函数(尾插),在链表尾部插入一个节点,本来应该直接传入一个链表的指针,但是考虑到如果传入的是一个空链表的指针,我们需要为该指针开辟一个空间来写入数据,就要改变该指针的值,如果直接传入该指针,我们只能改变该指针的形参的值,无法改变该指针的值,所以该函数应该传入链表指针的地址,即二级指针,如果传入的不是空链表,我们只需找到该链表的最后一个节点,为该节点的next开辟一个空间,将数据写进去就好了。
代码如下:
void PushBack(ListNode** ppList, DataType x)
{
//空
//非空
assert(ppList);
if(*ppList==NULL)
{
*ppList=BuyNode(x);
//当链表为空时,直接掉用BuyNode函数创建一个节点
}
else
{
ListNode* pList=*ppList;
while(pList->next)
{
pList=pList->next;
//当pList->next为空时,pList指向最后一个节点
}
pList->next=BuyNode(x);
//直接调用BuyNode函数在当前位置创建一个新节点
}
}
接下来实现PopBack函数(尾删),该函数也要传入一个二级指针,分为三种情况:
空链表:直接返回
链表只有一个节点:用free函数释放空间,并将链表指针置为空
链表含有多个节点:删除最后一个节点,并将前一个节点的next置为空,我们只需要用while循环,就可以很轻松的找到最后一个节点,难点在于将最后一个节点删除之后,如何找到前一个节点,在这里,我们可以创建两个指针p1,p2,始终让p2指向p1的下一个节点,这样但p2到达最后一个节点时,p1刚好指向上一个节点。
代码如下:
void PopBack(ListNode** ppList)
{
//空
//一个
//多个
assert(ppList);
if(*ppList==NULL)
return;//链表为空直接返回
else if((*ppList)->next ==NULL)
{
//只有一个节点
free(*ppList);//释放空间
*ppList=NULL;//链表指针置为空
}
else//含有多个节点
{
ListNode* p1=*ppList;
ListNode* p2=p1->next ;//p2指p1下一个节点
while(p2->next)//p2指向最后一个节点时退出循环
{
p1=p1->next ;
p2=p2->next ;
//p1,p2分别往后移动一个节点
}
free(p2);
p1->next =NULL;
}
PushFront函数(头插)
1、单链表为空,直接调用BuyNode函数创建一个节点
2、单链表不为空,创建一个新的节点赋给链表第一个节点的指针,并将next指向链表原来的第一个节点,只需要创建一个临时指针变量接收原来第一个节点的地址就可以了。
代码如下:
void PushFront(ListNode** ppList, DataType x)
{
assert(ppList);
//空
//非空
if(*ppList==NULL)
*ppList=BuyNode(x);
else
{
ListNode* p=*ppList;
*ppList=BuyNode(x);
(*ppList)->next =p;
}
}
PopFront函数(头删)
该函数依然需要传入二级指针,根据单链表不同有三种情况:
1、单链表为空,这种情况我们直接return;
2、单链表只有一个节点,删除该节点,将链表指针置为空,
3、单链表含有多个节点,删除第一个节点,并将链表指针指向下个节点。
代码如下:
void PopFront(ListNode** ppList)
{
assert(ppList);
if(*ppList==NULL)//空
return;
else if((*ppList)->next ==NULL)//一个
{
free(*ppList);
*ppList=NULL;
}
else//多个
{
ListNode* ptmp=*ppList;
*ppList=(*ppList)->next ;//*ppList指向下个节点
free(ptmp);//删除首个节点
}
}
Find函数(查找)
该函数要求传入一个单链表指针和一个数据x,在单链表中找到x的位置。
我们只需要将单链表遍历一遍,在此过程中找到x则返回它的指针,遍历玩仍未找到返回NULL;
代码如下:
ListNode* Find(ListNode* pList, DataType x)
{
while(pList)
{
if(pList->data ==x)
return pList;
pList=pList->next ;
}
return NULL;
}
Insert函数(在pos的前面插入一个节点x)
1、单链表为空,return
2、pos是第一个节点,调用头插函数
3、pos不是第一个节点,创建一个节点,让pos的前一个节点指向该节点,并使该节点指向pos
代码如下:
void Insert(ListNode** ppList, ListNode* pos, DataType x)
{
assert(ppList);
assert(pos);
if(*ppList==NULL)
return;
else if(*ppList==pos)
PushFront(ppList,x);
else
{
ListNode* pList=*ppList;
while((pList->next)!=pos)
{
pList=pList->next ;
}//找到pos的前一个节点
pList->next =BuyNode(x);
pList->next ->next =pos;
}
}
Erase函数(删除指定节点)
1、单链表或pos为空,return
2、pos为第一个节点指针,调用头删函数
3、pos不是第一个节点指针,删除该节点,并使该节点上个节点指向该节点下个节点(如果是最后一个节点则指向NULL)
代码如下:
void Erase(ListNode** ppList, ListNode* pos)
{
assert(ppList);
if((*ppList==NULL)||(pos==NULL))
return;
else if(*ppList==pos)
PopFront(ppList);
else
{
ListNode* pList=*ppList;
while((pList)->next !=pos)
{
pList=pList->next ;
//找到pos的前一个节点
}
pList->next =pos->next ;
free(pos);
}
}
至此,所有的函数都已经实现了,完整的list.c源文件代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include "list.h"
ListNode* BuyNode(DataType x)
{
ListNode *pList=(ListNode*)malloc(sizeof(ListNode));
pList->data=x;
pList->next=NULL;
return pList;
}
void PrintList(ListNode* pList)
{
while(pList)
{
printf("%d->",pList->data);
pList=pList->next;
}
printf("%s\n",pList);//此时pList为空,会输出(null)
}
void PushBack(ListNode** ppList, DataType x)
{
//空
//非空
assert(ppList);
if(*ppList==NULL)
{
*ppList=BuyNode(x);
//当链表为空时,直接掉用BuyNode函数创建一个节点
}
else
{
ListNode* pList=*ppList;
while(pList->next)
{
pList=pList->next;
//当pList->next为空时,pList指向最后一个节点
}
pList->next=BuyNode(x);
//直接调用BuyNode函数在当前位置创建一个新节点
}
}
void PopBack(ListNode** ppList)
{
//空
//一个
//多个
assert(ppList);
if(*ppList==NULL)
return;//链表为空直接返回
else if((*ppList)->next ==NULL)
{
//只有一个节点
free(*ppList);//释放空间
*ppList=NULL;//链表指针置为空
}
else//含有多个节点
{
ListNode* p1=*ppList;
ListNode* p2=p1->next ;//p2指p1下一个节点
while(p2->next)//p2指向最后一个节点时退出循环
{
p1=p1->next ;
p2=p2->next ;
//p1,p2分别往后移动一个节点
}
free(p2);
p1->next =NULL;
}
}
void PushFront(ListNode** ppList, DataType x)
{
assert(ppList);
//空
//非空
if(*ppList==NULL)
*ppList=BuyNode(x);
else
{
ListNode* p=*ppList;
*ppList=BuyNode(x);
(*ppList)->next =p;
}
}
void PopFront(ListNode** ppList)
{
assert(ppList);
if(*ppList==NULL)//空
return;
else if((*ppList)->next ==NULL)//一个
{
free(*ppList);
*ppList=NULL;
}
else//多个
{
ListNode* ptmp=*ppList;
*ppList=(*ppList)->next ;//*ppList指向下个节点
free(ptmp);//删除首个节点
}
}
ListNode* Find(ListNode* pList, DataType x)
{
while(pList)
{
if(pList->data ==x)
return pList;
pList=pList->next ;
}
return NULL;
}
void Insert(ListNode** ppList, ListNode* pos, DataType x)
{
assert(ppList);
assert(pos);
if(*ppList==NULL)
return;
else if(*ppList==pos)
PushFront(ppList,x);
else
{
ListNode* pList=*ppList;
while((pList->next)!=pos)
{
pList=pList->next ;
}//找到pos的前一个节点
pList->next =BuyNode(x);
pList->next ->next =pos;
}
}
void Erase(ListNode** ppList, ListNode* pos)
{
assert(ppList);
if((*ppList==NULL)||(pos==NULL))
return;
else if(*ppList==pos)
PopFront(ppList);
else
{
ListNode* pList=*ppList;
while((pList)->next !=pos)
{
pList=pList->next ;
}
pList->next =pos->next ;
free(pos);
}
}
test.c
test.c文件用来验证编写的函数,自己检验即可,我的代码如下:
#define _CRT_SECURE_NO_WARNINGS 1
#include "list.h"
void test1()//验证BuyNode,PrintList
{
ListNode* pList=BuyNode(1);
PrintList(pList);//1->(null)
}
void test2()//验证PushBack
{
//空
ListNode* pList=NULL;
PushBack(&pList,1);
PrintList(pList);//1
//非空
PushBack(&pList,2);
PushBack(&pList,3);
PushBack(&pList,4);
PrintList(pList);//1->2->3->4->(null)
}
void test3()//验证PopBack
{
ListNode* pList=BuyNode(1);
PushBack(&pList,2);
PushBack(&pList,3);
PushBack(&pList,4);
PrintList(pList);//1->2->3->4->(null)
PopBack(&pList);
PrintList(pList);//1->2->3->(null)
PopBack(&pList);
PrintList(pList);//1->2->(null)
PopBack(&pList);
PrintList(pList);//1->(null)
PopBack(&pList);
PrintList(pList);//(null)
PopBack(&pList);
PrintList(pList);//(null)
}
void test4()//验证PushFront
{
//空
ListNode* pList=NULL;
PushFront(&pList,1);
PrintList(pList);//1->(null)
//非空
PushFront(&pList,2);//2->1->(null)
PrintList(pList);
PushFront(&pList,3);//3->2->1->(null)
PrintList(pList);
PushFront(&pList,4);//4->3->2->1->(null)
PrintList(pList);
}
void test5()//验证PopFront
{
ListNode* pList=BuyNode(1);
PushBack(&pList,2);
PushBack(&pList,3);
PushBack(&pList,4);
PrintList(pList);//1->2->3->4->(null)
PopFront(&pList);
PrintList(pList);//2->3->4->(null)
PopFront(&pList);
PrintList(pList);//3->4->(null)
PopFront(&pList);
PrintList(pList);//4->(null)
PopFront(&pList);
PrintList(pList);//(null)
PopFront(&pList);
PrintList(pList);//(null)
}
void test6()//验证Find,Insert
{
ListNode* p;
ListNode* pList=BuyNode(1);
PushBack(&pList,2);
PushBack(&pList,3);
PushBack(&pList,4);
PrintList(pList);//1->2->3->4->(null)
p=Find(pList,3);//3->4->(null)
PrintList(p);
p=Find(pList,2);//2->3->4->(null)
PrintList(p);
p=Find(pList,5);//(null)
PrintList(p);
p=Find(pList,1);
Insert(&pList,p,5);
PrintList(pList);//5->1->2->3->4->(null)
p=Find(pList,3);
Insert(&pList,p,6);
PrintList(pList);//5->1->2->6->3->4->(null)
}
void test7()//验证Erase
{
ListNode* p;
ListNode* pList=NULL;
PushBack(&pList,1);
PushBack(&pList,2);
PushBack(&pList,3);
PushBack(&pList,4);
PrintList(pList);//1->2->3->4->(null)
p=Find(pList,3);
Erase(&pList,p);
PrintList(pList);//1->2->4->(null)
p=Find(pList,1);
Erase(&pList,p);
PrintList(pList);//2->4->(null)
p=Find(pList,4);
Erase(&pList,p);
PrintList(pList);//2->(null)
}
int main()
{
//test1();
//test2();
//test3();
//test4();
//test5();
//test6();
test7();
return 0;
}