C语言实现单链表(不带头结点)

      在之前的博客中,我写了有关线性表的顺序存储结构的顺序表,顺序表的特点是逻辑关系上,相邻的两个元素在物理位置上也相邻,因此可以随机存取表中任一元素,但是,它在进行插入删除操作时,需要移动大量的数据元素,故而在下面的内容中,讨论的是线性表的另一种存储结构(链式存储结构)中的不带头节点的单链表,由于链表不要求逻辑上相邻的元素在物理位置上也相邻,所以它没有顺序表的弱点,但是也失去了它的优点。

      附上我的有关顺序表的博客链接:

                     静态顺序表

      在下面的讨论中,我们讨论的是不带头结点的单链表,我们用一张图感受一下不带头结点的单链表如何存储数据:

              

      在下面的具体内容有:单链表结点的定义和单链表相关操作接口以及附上完整的代码,包括头文件,接口实现文件和测试文件,而操作接口包括:

      1、初始化  2、获得一个结点  3、尾插  4、尾删  5、头插  6、头删  7、查找指定元素  8、指定位置插入  9、指定位置删除  10、删除指定元素  11、删除所有指定元素  12、打印  13、销毁  14、获取链表长度

单链表结点的定义

       链表的定义:一种链式存储的线性表,用一组地址任意的存储单元存放线性表的数据元素,称存储单元为一个结点。

//结构体定义单链表的结点
typedef int DataType; 
typedef struct Node
{
    DataType data;   //当前节点所保存的元素
    struct Node* next;   //指向链表中下一个结点
}Node,List,*pNode,*pList;  

       在上面的定义中,我们使用了关键字 typedef 使得结点中元素类型为 DataType ,有助于之后修改数据类型;而将 struct Node 创建新名为 Node 和 List ,和将 struct Node* 创建新名为 pNode 和 pList ,是为了帮助我们书写、阅读和理解之后的代码。

单链表的相关操作接口

1、初始化

void InitList(pList* pplist)
{
    assert(pplist != NULL);
    *pplist = NULL;
}

注:pplist是外部传进来的一个变量的地址,不敢为空指针,故需断言。在之后的代码中就不解释了。

2、获得一个结点

    因为在之后的插入中,我们需要得到一个结点进行插入操作,所以我先把获得一个结点的代码写出,代码如下:

pNode BuyNode(DataType d)
{
    pNode tmp = (pNode)malloc(sizeof(Node));    //申请一个结点大小的空间
    //判断是否申请空间成功
    if (tmp == NULL)
    {
	perror("BuyNode::malooc");
	return NULL;
    }
    //给新的结点赋值
    tmp->data = d;
    tmp->next = NULL;
    return tmp;
}

3、尾插

      核心:找到最后一个结点

void PushBack(pList* pplist,DataType d)
{
    pNode newNode = BuyNode(d);   //获得一个新的结点,存放我们要插入的数据

    assert(pplist != NULL);

    //判断获取新的结点是否成功
    if (newNode == NULL)   
    {
        exit(EXIT_FAILURE);
    }
    //空链表
    if (*pplist == NULL)
    {
	*pplist = newNode;
    }
    //非空链表,遍历单链表找到尾结点,将尾结点指向新结点
    else    
    {
	pNode cur = *pplist;
	while (cur->next != NULL)
	{
	    cur = cur->next;
	}
	cur->next = newNode;
    }
}

4、尾删

     核心:找到倒数第二个结点

void PopBack(pList* pplist)
{
    assert(pplist != NULL);

    //链表为空,无可删除数据,直接返回
    if (*pplist == NULL)
    {
        return;
    }
    //只有一个结点
    if ((*pplist)->next == NULL)
    {
	free(*pplist);
	*pplist = NULL;
    }
    //一个以上的结点
    else
    {
	pNode cur = *pplist;
	while(cur->next->next != NULL)
	{
	    cur = cur->next;
	}
	//循环结束,cur指向倒数第二个结点,释放最后一个结点
	free(cur->next);
	cur->next = NULL;
    }
}

5、头插

     核心:无需考虑是否为空链表

void PushFront(pList* pplist, DataType d)
{
    pNode newNode = BuyNode(d);   //获得一个新结点

    assert(pplist != NULL);

    //判断是否申请成功
    if (newNode == NULL)
    {
	exit(EXIT_FAILURE);
    }
    //头插
    newNode->next = *pplist;
    *pplist = newNode;
}

6、头删

void PopFront(pList* pplist)
{
    assert(pplist != NULL);
    
    //链表为空,无可删除数据,直接返回
    if (*pplist == NULL)
    {
	return;
    }
    //链表不为空,将 pplist 指向第二个结点,此时第二个结点为第一个结点,需注意释放被删除的空间。
    else
    {
	pNode del = *pplist;
	*pplist = del->next;
	free(del);
	del = NULL;
    }
}

7、查找指定元素

    核心:返回的是所查找的元素的地址,方法是遍历查找。

pNode Find(pList plist, DataType d)
{
	pNode cur = plist;
	while (cur)
	{
		if (cur->data == d)
		{
			return cur;
		}
		cur = cur->next;
	}
	return NULL;   //没找到
}

8、指定位置插入

     核心:指定位置是否为第一个位置,不是的话,是否存在。

void Insert(pList* pplist, pNode pos, DataType d)
{
    pNode newNode = NULL;

    assert(pplist != NULL);
    assert(*pplist != NULL);
    assert(pos != NULL);

    //第一个位置——>头插
    if (pos == *pplist)
    {
        newNode = BuyNode(d);
        newNode->next = *pplist;
        *pplist = newNode;
    }
    else
    {
        pNode cur = *pplist;
        while (cur && cur->next != pos)  //顺序不可以颠倒
        {
            cur = cur->next;
        }
        if (cur != NULL)
        {
            newNode = BuyNode(d);
            newNode->next = pos;
            cur->next = newNode;
        }
    }
}

9、指定位置删除

    核心:同上,位置问题

void Erase(pList* pplist, pNode pos)
{
    assert(pplist != NULL);
    assert(pos != NULL);
    
    if (*pplist == NULL)
    {
        return;
    }
    //第一个结点
    if (*pplist == pos)
    {
        //头删
        pNode del = pos;
        *pplist = (*pplist)->next;
        free(del);
        del = NULL;
    }
    //不是第一个结点
    else
    {
        pNode cur = *pplist;
        while (cur&&cur->next != pos)
        {
            cur = cur->next;
        }
        if (cur != NULL)
        {
            cur->next = pos->next;
            free(pos);
            pos = NULL;
        }
    }
}

10、删除指定元素

    核心:根据前面写的查找指定元素和指定位置删除就可以实现

void Remove_1(pList* pplist, DataType d)
{
    pNode cur = NULL;
    pNode prev = NULL;

    assert(pplist != NULL);

    cur = *pplist;
    while (cur)
    {
        if (cur->data == d)
        {
            //是第一个结点
            if (*pplist == cur)
            {
                PopFront(pplist);
            }
            //非第一个结点
            else
            {
                prev->next = cur->next;
                free(cur);
                cur = NULL;
            }
            return;
        }
        else
        {
            prev = cur;
            cur = cur->next;
        }
    }
}

void Remove_2(pList* pplist, DataType d)
{
    pNode pos = NULL;

    assert(pplist != NULL);

    pos = Find(*pplist, d);
    if (pos != NULL)
    {
        Erase(pplist, pos);
    }
}

11、删除所有指定元素 

       核心:与删除指定元素略有差异,需注意删除一个以后,将cur指向删除元素后一位。

void RemoveAll(pList* pplist, DataType d)
{
    pNode cur = NULL;
    pNode prev = NULL;

    assert(pplist != NULL);

    cur = *pplist;
    while (cur)
    {
        if (cur->data == d)
        {
            //是第一个结点
            if (*pplist == cur)
            {
                PopFront(pplist);
                cur = *pplist;
            }
            //非第一个结点
            else
            {
                prev->next = cur->next;
                free(cur);
                cur = prev;
            }
        }
        else
        {
            prev = cur;
            cur = cur->next;
        }
    }
}

12、打印

void PrintList(pList plist)
{
    //无需断言,允许链表为空

    //遍历打印
    pNode cur = plist;
    while (cur)
    {
	printf("%d--->", cur->data);
	cur = cur->next;
    }
    printf("over\n");
}

13、销毁

void DestroyList(pList* pplist)
{
    pNode cur = *pplist;

    assert(pplist != NULL);

    //从前向后销毁
    while (cur != NULL)
    {
	pNode del = cur;   //保存要销毁的结点
	cur = cur->next;   //使cur下一个要被销毁的结点
	free(del);
	del = NULL;
    }
    *pplist = NULL;
}

14、获取链表长度

int GetListLength(pList plist)
{
    pNode cur = plist;
    int count = 0;   //计数
    while (cur)   //遍历
    {
	count++;
	cur = cur->next;
    }
    return count;
}

    附上完整代码

//LinkList.h

#ifndef __LINKLIST_H__
#define __LINKLIST_H__

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef int DataType;
typedef struct Node
{
    DataType data;
    struct Node* next;
}Node, *pNode, List, *pList;

void InitList(pList* pplist);
pNode BuyNode(DataType d);
void PushBack(pList* pplist,DataType d);
void PopBack(pList* pplist);
void PushFront(pList* pplist, DataType d);
void PopFront(pList* pplist);
pNode Find(pList plist, DataType d);
void Insert(pList* pplist,pNode pos, DataType d);
void Erase(pList* pplist, pNode pos);
void Remove_1(pList* pplist, DataType d);
void Remove_2(pList* pplist, DataType d);
void RemoveAll(pList* pplist, DataType d);
void PrintList(pList plist);
void DestroyList(pList* pplist);
int GetListLength(pList plist);

#endif //__LINKLIST_H__
LinkList.c

#include "LinkList.h"

void InitList(pList* pplist)
{
    assert(pplist != NULL);   
    *pplist = NULL;
}

pNode BuyNode(DataType d)
{
    pNode tmp = (pNode)malloc(sizeof(Node));
    if (tmp == NULL)
    {
        perror("BuyNode::malooc");
        return NULL;
    }
    tmp->data = d;
    tmp->next = NULL;
    return tmp;
}

void PushBack(pList* pplist,DataType d)
{
    pNode newNode = BuyNode(d);

    assert(pplist != NULL);

    if (newNode == NULL)
    {
        exit(EXIT_FAILURE);
    }
    if (*pplist == NULL)   //空链表
    {
        *pplist = newNode;
    }
    else
    {
        pNode cur = *pplist;
        while (cur->next != NULL)
        {
            cur = cur->next;
        }
        cur->next = newNode;
    }
}

void PopBack(pList* pplist)
{
    assert(pplist != NULL);

    //链表为空,无可删除数据,直接返回
    if (*pplist == NULL)
    {
        return;
    }
    //只有一个结点
    if ((*pplist)->next == NULL)
    {
        free(*pplist);
        *pplist = NULL;
    }
    //一个以上的结点
    else
    {
        pNode cur = *pplist;
        while(cur->next->next != NULL)
        {
            cur = cur->next;
        }
        //循环结束,cur指向倒数第二个结点,释放最后一个结点
        free(cur->next);
        cur->next = NULL;
    }
}

void PushFront(pList* pplist, DataType d)
{
    pNode newNode = BuyNode(d);

    assert(pplist != NULL);

    if (newNode == NULL)
    {
        exit(EXIT_FAILURE);
    }
    newNode->next = *pplist;
    *pplist = newNode;
}

void PopFront(pList* pplist)
{
    assert(pplist != NULL);

    if (*pplist == NULL)
    {
        return;
    }
    else
    {
	pNode del = *pplist;
	*pplist = del->next;
	free(del);
	del = NULL;
    }
}

pNode Find(pList plist, DataType d)
{
    pNode cur = plist;
    while (cur)
    {
        if (cur->data == d)
        {
            return cur;
        }
        cur = cur->next;
    }
    return NULL;
}

void Insert(pList* pplist, pNode pos, DataType d)
{
    pNode newNode = NULL;

    assert(pplist != NULL);
    assert(*pplist != NULL);
    assert(pos != NULL);

    //第一个位置——>头插
    if (pos == *pplist)
    {
        newNode = BuyNode(d);
        newNode->next = *pplist;
        *pplist = newNode;
    }
    else
    {
        pNode cur = *pplist;
        while (cur && cur->next != pos)  //顺序不可以颠倒
        {
            cur = cur->next;
        }
        if (cur != NULL)
        {
            newNode = BuyNode(d);
            newNode->next = pos;
            cur->next = newNode;
        }
    }
}

void Erase(pList* pplist, pNode pos)
{
    assert(pplist != NULL);
    assert(pos != NULL);
    
    if (*pplist == NULL)
    {
        return;
    }
    //第一个结点
    if (*pplist == pos)
    {
        //头删
        pNode del = pos;
        *pplist = (*pplist)->next;
        free(del);
        del = NULL;
    }
    //不是第一个结点
    else
    {
        pNode cur = *pplist;
        while (cur&&cur->next != pos)
        {
            cur = cur->next;
        }
        if (cur != NULL)
        {
            cur->next = pos->next;
            free(pos);
            pos = NULL;
        }
    }
}

void Remove_1(pList* pplist, DataType d)
{
    pNode cur = NULL;
    pNode prev = NULL;

    assert(pplist != NULL);

    cur = *pplist;
    while (cur)
    {
        if (cur->data == d)
        {
            //是第一个结点
            if (*pplist == cur)
            {
                PopFront(pplist);
            }
            //非第一个结点
            else
            {
                prev->next = cur->next;
                free(cur);
                cur = NULL;
            }
            return;
        }
        else
        {
            prev = cur;
            cur = cur->next;
        }
    }
}

void Remove_2(pList* pplist, DataType d)
{
    pNode pos = NULL;

    assert(pplist != NULL);

    pos = Find(*pplist, d);
    if (pos != NULL)
    {
        Erase(pplist, pos);
    }
}

void RemoveAll(pList* pplist, DataType d)
{
    pNode cur = NULL;
    pNode prev = NULL;

    assert(pplist != NULL);

    cur = *pplist;
    while (cur)
    {
        if (cur->data == d)
        {
            //是第一个结点
            if (*pplist == cur)
            {
                PopFront(pplist);
                cur = *pplist;
            }
            //非第一个结点
            else
            {
                prev->next = cur->next;
                free(cur);
                cur = prev;
            }
        }
        else
        {
            prev = cur;
            cur = cur->next;
        }
    }
}

void PrintList(pList plist)
{
    pNode cur = plist;
    while (cur)
    {
    printf("%d--->", cur->data);
    cur = cur->next;
    }
    printf("over\n");
}

void DestroyList(pList* pplist)
{
    pNode cur = *pplist;

    assert(pplist != NULL);

    while (cur != NULL)
    {
        pNode del = cur;
        cur = cur->next;
        free(del)
        del = NULL;
    }
    *pplist = NULL;
}

int GetListLength(pList plist)
{
    pNode cur = plist;
    int count = 0;
    while (cur)
    {
        count++;
        cur = cur->next;
    }
    return count;
}

 

test.c

#include "LinkList.h"

void TestPushBack()
{
    pList plist = NULL;
    InitList(&plist);
    PushBack(&plist, 1);
    PushBack(&plist, 2);
    PushBack(&plist, 3);
    PushBack(&plist, 4);
    PrintList(plist);  //1  2  3  4
    printf("len = %d\n", GetListLength(plist));  //4
    DestroyList(&plist);
    PrintList(plist);  //
}

void TestPopBack()
{
    pList plist = NULL;
    InitList(&plist);
    PushBack(&plist, 1);
    PushBack(&plist, 2);
    PushBack(&plist, 3);
    PushBack(&plist, 4);
    PrintList(plist);   //1  2  3  4
    PopBack(&plist);  
    PrintList(plist);   //1  2  3
    PopBack(&plist);  
    PrintList(plist);   //1  2
    PopBack(&plist);  
    PrintList(plist);   //1
    PopBack(&plist);  
    PrintList(plist);   //
    DestroyList(&plist);
    PrintList(plist);   //
}

void TestPushFront()
{
    pList plist = NULL;
    InitList(&plist);
    PushFront(&plist, 1);
    PushFront(&plist, 2);
    PushFront(&plist, 3);
    PushFront(&plist, 4);
    PrintList(plist);   //4  3  2  1
    DestroyList(&plist);
    PrintList(plist);  //
}

void TestPopFront()
{
    pList plist = NULL;
    InitList(&plist);
    PushFront(&plist, 1);
    PushFront(&plist, 2);
    PushFront(&plist, 3);
    PushFront(&plist, 4);
    PrintList(plist);   //4  3  2  1
    PopFront(&plist);
    PrintList(plist);   //3  2  1
    PopFront(&plist);
    PrintList(plist);   //2  1
    PopFront(&plist);
    PrintList(plist);   //1
    PopFront(&plist);
    PrintList(plist);   //
    DestroyList(&plist);
    PrintList(plist);  //
}

void TestFind()
{
    pList plist = NULL;
    pNode pos = NULL;
    InitList(&plist);
    PushFront(&plist, 1);
    PushFront(&plist, 2);
    PushFront(&plist, 3);
    PushFront(&plist, 4);
    PrintList(plist);   //4  3  2  1
    pos = Find(plist, 3);
    if (pos != NULL)
    {
        printf("%d\n", pos->data);
    }
    DestroyList(&plist);
    PrintList(plist);  //
}

void TestInsert()
{
    pList plist = NULL;
    pNode pos = NULL;
    InitList(&plist);
    PushFront(&plist, 1);
    PushFront(&plist, 2);
    PushFront(&plist, 3);
    PushFront(&plist, 4);
    PrintList(plist);   //4  3  2  1
    pos = Find(plist, 3);
    if (pos != NULL)
    {
        Insert(&plist, pos, 5);
    }
    PrintList(plist);   //4  5  3  2  1
    pos = Find(plist, 4);
    if (pos != NULL)
    {
        Insert(&plist, pos, 6);
    }
    PrintList(plist);   //6  4  5  3  2  1
    DestroyList(&plist);
    PrintList(plist);  //
}

void TestErase()
{
    pList plist = NULL;
    pNode pos = NULL;
    InitList(&plist);
    PushFront(&plist, 1);
    PushFront(&plist, 2);
    PushFront(&plist, 3);
    PushFront(&plist, 4);
    PrintList(plist);   //4  3  2  1
    pos = Find(plist, 3);
    if (pos != NULL)
    {
        Erase(&plist, pos);
    }
    PrintList(plist);   //4  2  1
    pos = Find(plist, 4);
    if (pos != NULL)
    {
        Erase(&plist, pos);
    }
    PrintList(plist);   //2  1
    DestroyList(&plist);
    PrintList(plist);  //
}

void TestRemove()
{
    pList plist = NULL;
    InitList(&plist);
    PushFront(&plist, 1);
    PushFront(&plist, 2);
    PushFront(&plist, 3);
    PushFront(&plist, 1);
    PushFront(&plist, 4);
    PushFront(&plist, 2);
    PushFront(&plist, 3);
    PrintList(plist);   //3  2  4  1  3  2  1
    Remove_1(&plist, 2);
    PrintList(plist);   //3  4  1  3  2  1
    Remove_2(&plist, 3);
    PrintList(plist);   //4  1  3  2  1
    RemoveAll(&plist, 1);
    PrintList(plist);   //4  3  2 
    DestroyList(&plist);
    PrintList(plist);  //
}

int main()
{
    TestPushBack();
    TestPopBack();
    TestPushFront();
    TestPopFront();
    TestFind();
    TestInsert();
    TestErase();
    TestRemove();

    system("pause");
    return 0;
}

 

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值