C语言实现单链表

单链表是一种链式存取的数据结构,,其中数据以节点来表示,每个节点由一个数据元素和一个指向后继元素存储位置的指针构成,在这里,我通过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;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值