您的位置:首页 > 编程语言 > C语言/C++

C语言实现单链表

2017-06-28 17:15 246 查看
单链表是一种链式存取的数据结构,,其中数据以节点来表示,每个节点由一个数据元素和一个指向后继元素存储位置的指针构成,在这里,我通过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函数(头删)

该函数依然需要传入二级指针,根据单
1032c
链表不同有三种情况:

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  c语言 数据结构