单链表(不带头结点不带环)的相关操作基于C语言实现

不带头结点不带环的单链表

 链表按照:单向链表、双向链表;带环链表、不带环链表;带头结点、不带头结点;共有八种,最常见的即不带头结点不带环的单链表的相关操作实现。

1. 不带头结点:用NULL表示空链表;

2. 用第一个结点表示整个链表。因为知道第一个结点,从而通过其next域可以找到单链表的其他结点;
3. 最后一个结点的next指向NULL;
4. 单链表除首元素结点外的每个结点都有直接前驱,除最后一个结点外的每个结点都有直接后继;
Linklist.h  
        首先,我们先将单链表的结点的结构体表示出来,一个结点包括它的数值,再有一个指向下一个结点的指针,所以定义结构体结点如下:
//单向不带环不带头结点链表实现

#pragma once

#include <stdio.h>
#include <stdlib.h>//malloc头文件
#include <stddef.h>//sizeof

#define SHOW_NAME printf("\n==========%s==========\n",__FUNCTION__)

typedef char LinkNodeType;

typedef struct LinkNode//单链表的结点
{
    LinkNodeType data;
    struct LinkNode* next;
}LinkNode;//*pLinkNode;

1. 链表的初始化
void LinklistInit(LinkNode** head)//初始化
{
    *head = NULL;
}

2.创建一个新结点
LinkNode* CreateNode(LinkNodeType value)//创建新结点
{
    LinkNode* new_node = (LinkNode* )malloc(sizeof(LinkNode));//sizeof是一个运算符
    new_node->data = value;
    new_node->next = NULL;
    return new_node;
}

3.单链表的尾插
void LinklistPushBack(LinkNode** head,LinkNodeType value)//尾插
{
    if(head == NULL)//说明head结点不存在
        return;
    if(*head == NULL)//空链表
    {
        *head = CreateNode(value);
        return;
    }
    LinkNode* cur = *head;//链表非空
    while(cur->next != NULL)
    {
        cur = cur->next;
    }
    LinkNode* new_node = CreateNode(value);
    cur->next = new_node;
    //此处不用将new_node的next置空,因为CreateNode中已实现
}

4.单链表的尾删
void LinklistPopBack(LinkNode** head)//尾删
{
    if(head == NULL)//非法操作
        return;
    if(*head == NULL)//空链表
        return;
    if((*head)->next == NULL)//只有一个元素
    {
        DestroyNode(*head);
        *head = NULL;
        return;
    }
    LinkNode* pre = NULL;//pre指向倒数第二个结点
    LinkNode* cur = *head;
    while(cur->next != NULL)
    {
        pre = cur;
        cur = cur->next;
    }
    pre->next = NULL;
    DestroyNode(cur);
}

5.单链表的头插
void LinklistPushFront(LinkNode** head,LinkNodeType value)//头插
{
    if(head == NULL)//非法操作
        return;
    LinkNode* new_node = CreateNode(value);//链表是否为空都适用
    new_node->next = *head;
    *head = new_node;
    return;
}

6.单链表的头删
void LinklistPopFront(LinkNode** head)//头删
{
    if(head == NULL)
        return;
if(*head == NULL)
        return;
    LinkNode* cur = *head;//不论链表里有多少个结点,都适用
    *head = (*head)->next;
    DestroyNode(cur);
    return;
}

7.给定一个值,在单链表中查找对应结点
LinkNode* LinklistFind(LinkNode* head,LinkNodeType value)//给定一个值,在单链表中查找对应结点
{
    if(head == NULL)//空链表
        return;
    LinkNode* cur = head;
    while(cur != NULL)
    {
        if(cur->data == value)
            return cur;
        cur = cur->next;
    }
    return NULL;
}

8.在单链表的任意位置之后插入结点
void LinklistInsertAfter(LinkNode* pos,LinkNodeType value)//在pos位置之后插入元素
{
    if(pos == NULL)//非法操作,pos不存在
        return;
    LinkNode* new_node = CreateNode(value);
    new_node->next = pos->next;
    pos->next = new_node;
    return;
}

9. 在单链表的任意位置之后前入结点
void LinklistInsertBefore(LinkNode** head,LinkNode* pos,LinkNodeType value)//在pos位置之前插入元素
{
    if(head == NULL || pos == NULL)//非法操作
        return;
    if(*head == pos)//插入位置为头结点之前
    {
        LinklistPushFront(head,value);
        return;
    }
    LinkNode* cur = *head;
    for(; cur!=NULL; cur=cur->next)
    {
        if(cur->next == pos)
            break;
    }
    if(cur == NULL)//没找到pos
        return;
    LinklistInsertAfter(cur,value);
}

10.删除指定结点
方法一:
void LinklistErase(LinkNode** head,LinkNode* pos)//删除pos结点的O(n)算法
{
    if(head == NULL || pos == NULL)
        return;
    if(*head == NULL)
        return;
    LinkNode* cur = *head;
    if(cur == pos)
    {
        LinkNode* to_delete = cur;
        *head = cur->next;
        DestroyNode(to_delete);
        return;
    }
    for(; cur->next!= NULL; cur=cur->next)
    {
        if(cur->next == pos)
            break;
    }
    if(cur == NULL)//未找到pos结点
        return;
    cur->next = pos->next;
    DestroyNode(pos);
}

方法二:
void LinklistErase1(LinkNode** head,LinkNode* pos)
{
    if(head == NULL || pos == NULL)
        return;
    if(*head == NULL)
        return;
    if(pos->next == NULL)//要删结点为最后一个结点
    {
        LinklistPopBack(head);
        return;
    }
    if(pos == *head)
    {
        LinklistPopFront(head);
        return;
    }
    pos->data = pos->next->data;//将pos后结点的值赋给pos,再删除pos后的结点即可
    LinkNode* cur = pos->next;
    pos->next = cur->next;
    DestroyNode(cur);//此代码不适用删除非链表中的有效元素
}

11.给定一个值,删除对应结点(若有相同的,仅删除第一个结点)
void LinklistRemove(LinkNode** head,LinkNodeType value)//删除指定值的第一个结点
{
    if(head == NULL)
        return;
    if(*head == NULL)
        return;
    LinkNode* cur = *head;
    for(; cur!=NULL; cur=cur->next)
    {
        if(cur->data == value)
        {
            LinklistErase(head,cur);
            return;
        }
    }
}
12.给定一个值,删除对应结点(若有相同的,删除所有结点)
void LinklistRemoveAll(LinkNode** head,LinkNodeType value)//删除指定值的所有结点
{
    if(head == NULL)
        return;
    if(*head == NULL)
        return;
    LinkNode* cur = *head;
    LinkNode* pre = NULL;
    while(cur != NULL)
    {
        if(cur->data == value)
        {
            if(cur == *head)//要删的为首元素结点
            {
                LinkNode* to_delete = cur;
                cur = *head = cur->next;
                DestroyNode(to_delete);
            }
            else if(cur->next == NULL)//要删的为最后一个结点
            {
                pre->next = NULL;
                DestroyNode(cur);
            }
            else
            {
                LinkNode* to_delete = cur;
                cur = cur->next;
                pre->next = cur;
                DestroyNode(to_delete);
            }
        }
        else
        {
            pre = cur;
            cur = cur->next;
        }
    }
   return;
}

13.单链表置空
int LinklistEmpty(LinkNode* head)
{
    if(head == NULL)
        return 1;
    return 0;
}

14.销毁结点
void DestroyNode(LinkNode* head)//销毁一个结点
{
    free(head);
}

15.求链表的大小
size_t LinklistSize(LinkNode* head)
{
    if(head == NULL)
        return 0;
    LinkNode* cur = head;
    size_t count = 0;
    while(cur != NULL)
    {
        count++;
        cur = cur->next;
    }
    return count;
}

16.单链表的打印
void LinklistPrintChar(LinkNode* head,const char* msg)//打印函数
{
    printf("[%s]\n",msg);
    if(head == NULL)//空链表
    {
        printf("链表为空\n");
        return;
    }
    LinkNode* cur = head;
    for(; cur!=NULL; cur=cur->next)
    {
        printf("[%c|%p] ",cur->data,cur);//打印值和地址
    }
    printf("\n");
}

17.单链表的销毁
void LinklistDestroy(LinkNode** head)//销毁链表
{
    if(head == NULL)
        return;
    if(*head == NULL)
        return;
    LinkNode* cur = *head;
    while(cur != NULL)
    {
        LinkNode* to_delete = cur;
        cur = cur->next;
        DestroyNode(to_delete);
    }
    *head = NULL;
//检查内存泄漏的方法:程序执行前查看程序占用内存的情况,多次调用该函数后再次检查内存
}

以下为以上函数的测试代码:
void TestInit()
{
    SHOW_NAME;
    LinkNode* head;
    LinklistInit(&head);
}

void TestPushBack()
{
    SHOW_NAME;
    LinkNode* head;
    LinklistInit(&head);
    LinklistPushBack(&head,'a');
    LinklistPushBack(&head,'b');
    LinklistPushBack(&head,'c');
    LinklistPushBack(&head,'d');
    LinklistPrintChar(head,"尾插四个元素");
}

void TestPopBack()
{
    SHOW_NAME;
    LinkNode* head;
    LinklistInit(&head);
    LinklistPushBack(&head,'a');
    LinklistPushBack(&head,'b');
    LinklistPushBack(&head,'c');
    LinklistPushBack(&head,'d');
    LinklistPrintChar(head,"尾插四个结点");
    LinklistPopBack(&head);
    LinklistPrintChar(head,"尾删一个结点");
}

void TestPushFront()
{
    SHOW_NAME;
    LinkNode* head;
    LinklistInit(&head);
    LinklistPushFront(&head,'a');
    LinklistPushFront(&head,'b');
    LinklistPushFront(&head,'c');
    LinklistPushFront(&head,'d');
    LinklistPrintChar(head,"头插四个结点");
}

void TestPopFront()
{
    SHOW_NAME;
    LinkNode* head;
    LinklistInit(&head);
    LinklistPushFront(&head,'a');
    LinklistPushFront(&head,'b');
    LinklistPushFront(&head,'c');
    LinklistPushFront(&head,'d');
    LinklistPrintChar(head,"头插四个结点");
    LinklistPopFront(&head);
    LinklistPrintChar(head,"头删一个结点");
}

void TestFind()
{
    SHOW_NAME;
    LinkNode* head;
    LinklistInit(&head);
    LinklistPushFront(&head,'a');
    LinklistPushFront(&head,'b');
    LinklistPushFront(&head,'c');
    LinklistPushFront(&head,'d');
    LinklistPrintChar(head,"头插四个结点");
    LinkNode* ret = LinklistFind(head,'c');
    printf("[c的地址为%p]\n",ret);
}

void TestInsertAfter()
{
    SHOW_NAME;
    LinkNode* head;
    LinklistInit(&head);
    LinklistPushBack(&head,'a');
    LinklistPushBack(&head,'b');
    LinklistPushBack(&head,'c');
    LinklistPushBack(&head,'d');
    LinklistPrintChar(head,"尾插四个元素");
    LinkNode* cur = head->next;
    LinklistInsertAfter(cur,'x');
    LinklistPrintChar(head,"向b后插入x");
}

void TestInsertBefore()
{
    SHOW_NAME;
    LinkNode* head;
    LinklistInit(&head);
    LinklistPushBack(&head,'a');
    LinklistPrintChar(head,"尾插一个元素");
    LinklistInsertBefore(&head,head,'h');
    LinklistPrintChar(head,"在a前插入h");
    LinklistPushBack(&head,'b');
    LinklistPushBack(&head,'c');
    LinklistPushBack(&head,'d');
    LinklistPrintChar(head,"再尾插三个元素");
    LinkNode* cur = head->next->next;
    LinklistInsertBefore(&head,cur,'x');
    LinklistPrintChar(head,"向b前插入x");
}

void TestErase()
{
    SHOW_NAME;
    LinkNode* head;
    LinklistInit(&head);
    LinklistPushBack(&head,'a');
    LinklistPushBack(&head,'b');
    LinklistPushBack(&head,'c');
    LinklistPushBack(&head,'d');
    LinklistPrintChar(head,"尾插四个元素");
    LinkNode* cur = head->next->next;
    LinklistErase(&head,cur);
    LinklistPrintChar(head,"删除c");
    cur = head;
    LinklistErase(&head,cur);
    LinklistPrintChar(head,"删除a");
}

void TestErase1()
{
    SHOW_NAME;
    LinkNode* head;
    LinklistInit(&head);
    LinklistPushBack(&head,'a');
    LinklistPushBack(&head,'b');
    LinklistPrintChar(head,"尾插两个元素");
    LinkNode* cur = head->next;
    LinklistErase1(&head,cur);
    LinklistPrintChar(head,"删除b");
    LinklistPushBack(&head,'c');
    LinklistPushBack(&head,'d');
    LinklistPushBack(&head,'f');
    LinklistPrintChar(head,"再尾插三个元素");
    cur = head->next;
    LinklistErase1(&head,cur);
    LinklistPrintChar(head,"删除c");
    cur = head;
    LinklistErase1(&head,cur);
    LinklistPrintChar(head,"删除a");
}

void TestRemove()
{
    SHOW_NAME;
    LinkNode* head;
    LinklistInit(&head);
    LinklistPushBack(&head,'a');
    LinklistPushBack(&head,'b');
    LinklistPushBack(&head,'c');
    LinklistPushBack(&head,'d');
    LinklistPrintChar(head,"尾插四个元素");
    LinklistRemove(&head,'c');
    LinklistPrintChar(head,"删除c");
    LinklistRemove(&head,'x');
    LinklistPrintChar(head,"删除x");
}

void TestRemoveAll()
{
    SHOW_NAME;
    LinkNode* head;
    LinklistInit(&head);
    LinklistPushBack(&head,'d');
    LinklistPushBack(&head,'d');
    LinklistPushBack(&head,'d');
    LinklistPushBack(&head,'a');
    LinklistPushBack(&head,'d');
    LinklistPushBack(&head,'f');
    LinklistPrintChar(head,"尾插六个元素");
    LinklistRemoveAll(&head,'d');
    LinklistPrintChar(head,"删除d");
}

void TestEmpty()
{
    SHOW_NAME;
    LinkNode* head;
    LinklistInit(&head);
    size_t ret = LinklistEmpty(head);
    if(ret == 1)
        printf("链表为空\n");
    else
        printf("链表不为空\n");
    LinklistPushBack(&head,'c');
    LinklistPushBack(&head,'d');
    LinklistPrintChar(head,"尾插两个元素");
    ret = LinklistEmpty(head);
    if(ret == 1)
        printf("链表为空\n");
    else
        printf("链表不为空\n");
}

void TestSize()
{
    SHOW_NAME;
    LinkNode* head;
    LinklistInit(&head);
    size_t ret = LinklistSize(head);
    printf("expected 0,actual is %u\n",ret);
    LinklistPushBack(&head,'a');
    LinklistPushBack(&head,'b');
    LinklistPushBack(&head,'c');
    LinklistPushBack(&head,'d');
    LinklistPrintChar(head,"尾插四个元素");
    ret = LinklistSize(head);
    printf("expected 4,actual is %u\n",ret);
}

void TestDestroy()//销毁链表
{
    SHOW_NAME;
    LinkNode* head;
    LinklistInit(&head);
    LinklistPushBack(&head,'a');
    LinklistPushBack(&head,'b');
    LinklistPushBack(&head,'c');
    LinklistPushBack(&head,'d');
    LinklistPrintChar(head,"尾插四个元素");
    LinklistDestroy(&head);
    LinklistPrintChar(head,"销毁链表");
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值