单链表C语言实现及原理

单链表C语言实现及原理

一、简述
本文着重描述如何用C语言实现单链表的过程和原理,它包括了单链表所有的基础功能增、删、查、改。
关于单链表的定义就不在此赘述了。
本文是在参考了C语言单链表的基本操作总结(增删改查) 这篇文章之后,做出了一些优化,修复了部分bug,重新梳理了流程,使之更加简洁和容易理解,再次感谢此文作者。
代码如有任何错误或不妥,欢迎大家评论,给出宝贵建议。

二、代码
以下所有代码都保存在同一个.C文件里面。
1、包含头文件

/*
 * @Author: Dennis Chow 
 * @Date: 2020-07-16 16:11:22 
 * @Last Modified time: 2020-07-16 16:37:08
 */
#include <stdio.h>
#include <stdlib.h>

2、添加定义

#define uint unsigned int

//节点结构体
typedef struct NODE
{
    int data;           // 数据
    struct NODE *next;  // 节点指针
} Node;

Node *headNode = NULL;  // 头节点
Node *endNode = NULL;   // 尾节点

3、函数声明

Node* CreateNode(int data);             // 创建节点
void AddNode(int data);                 // 增加节点
uint GetListSize(void);                 // 获取大小
Node* FindNode(uint index);             // 查找节点
void ReadNode(uint index);              // 读取节点
void ReadList(void);                    // 读取链表
void SetNode(uint index, int data);     // 设置节点
void DeleteList(void);                  // 删除链表
void DeleteNode(uint index);            // 删除节点
void InsertNode(uint index, int data);  // 插入节点

4、主函数
单链表所有的操作增、删、查、改都在此执行。

int main() 
{
    // 增加节点
    AddNode(11);
    AddNode(22);
    AddNode(33);
    AddNode(44);
    AddNode(55);
    AddNode(66);

    // 设置节点
    SetNode(3, 99);

    // 获取大小
    printf("Linked list size: %d\n\n", GetListSize());

    // 读取链表
    ReadList();

    // 读取节点
    ReadNode(2);    

    // 删除节点
    DeleteNode(0);  // 头节点
    DeleteNode(2);  // 中间节点
    DeleteNode(GetListSize() - 1);  // 尾节点

    // 获取大小
    printf("Linked list Size: %d\n\n", GetListSize());

    // 插入节点
    InsertNode(0, 7);    // 头节点后插入
    InsertNode(2, 8);    // 中间节点后插入
    InsertNode(GetListSize() - 1, 9);    // 尾节点后插入

    // 读取链表
    ReadList();

    // 删除链表
    DeleteList();

    return 0;
}

5、创建节点
使用malloc函数为一个单链表节点申请一片内存,但此时还没有添加到单链表里面去,仅仅只是一个孤立的节点。

Node* CreateNode(int data)
{
    Node *node = (Node *)malloc(sizeof(Node));
    node->data = data;
    node->next = NULL;

    return node;
}          

6、增加节点
利用CreateNode函数创建一个节点,并增加到单链表中。

void AddNode(int data)
{
    Node *node = CreateNode(data);

    if (headNode == NULL)   //链表为空
    {
        headNode = node;
    }
    else  //链表已存在节点
    {
        endNode->next = node;
    }
    
    endNode = node;
}       

7、获取大小
获取单链表节点数量。

uint GetListSize(void)
{
    Node *node = headNode;
    uint size = 0;

    while (node != NULL)
    {
        node = node->next;
        size++;
    }
    
    return size;
}  

8、查找节点
通过index找到节点并返回。

Node* FindNode(uint index)
{
    Node *node = headNode;
    uint currentIndex = 0;

    if (node == NULL)
    {
        printf("链表为空!\n");
        return NULL;
    }        

    while (node != NULL)
    {
        if (index == currentIndex)
        {
            return node;
        }
        
        node = node->next;
        currentIndex++;
    }

    return NULL;
}   

9、读取节点
通过index读取节点的数据并打印出来。

void ReadNode(uint index)
{
    Node *node = FindNode(index);   // 查找节点

    if (node == NULL)
    {
        printf("无此节点!\n");
    }        
	else
	{
		printf("%s: Node%d's data = %d\n\n", __FUNCTION__, index, node->data);
	}    
}              

10、读取链表
遍历整个单链表并打印出来。

void ReadList(void)              
{
    Node *node = headNode;
    if (node == NULL)
    {
        printf("链表为空!\n");
    }
    else
    {
        for (int index = 0; index < GetListSize(); index++)
        {
            printf("%s: Node%d's data = %d\n", __FUNCTION__, index, node->data);
            node = node->next;
        }
        printf("\n");
    }
}

11、设置节点
通过index找到节点,并将节点的数据设置为data

void SetNode(uint index, int data)
{
    Node *node = FindNode(index);   // 查找节点 

    if (node == NULL)
    {
        printf("无此节点!\n");
        return;
    }        

    node->data = data;
}

12、删除链表
删除整个单链表。

void DeleteList(void)
{
    Node *node = headNode;

    if (node == NULL)
    {
        printf("链表为空!\n");
        return;
    }

    while (node != NULL)
    {
        Node *temp = node;
        node = node->next;
        free(temp);
    }

    headNode = NULL;
    endNode = NULL;
        
    printf("%s: The LINKED LIST has cleaned.\n", __FUNCTION__);
}       

13、删除节点
通过index删除节点,这个函数也是整个单链表最复杂的操作了。

void DeleteNode(uint index)
{
    Node *foundNode = FindNode(index);   // 查找节点

    if (foundNode == NULL)  // 无此节点
    {
        return;
    }
    else
    {
        Node *node = headNode;

        if (headNode == endNode)    // 只有一个节点
        {
            free(headNode);
            headNode = NULL;
            endNode = NULL;
        }
        else    // 大于一个节点
        {

            if (foundNode == headNode) // 删除头节点
            {
                Node *oldHeadNode = headNode;   // 暂存旧节点
                headNode = headNode->next;      // 将第二个节点设置为新头节点
                free(oldHeadNode);              // 释放头旧节点的内存
            }
            else if (foundNode == endNode)   // 删除尾节点
            {
                while (node->next != foundNode) // 找到要删除的节点
                {
                    node = node->next;
                }

                // 将倒数第二个节点的next置为NULL后就相当于设置为尾节点
                node->next = NULL;  
                free(foundNode);    //释放被删节点的内存
            }
            else    // 删除中间个某节点
            {
                while (node->next != foundNode) // 找到要删除的节点
                {
                    node = node->next;
                }

                // 将 被删节点 的 上一个节点的next 设置为 被删节点 的 下一个节点
                node->next = foundNode->next;   
                free(foundNode);    //释放被删节点的内存
            }
        }
    }

    printf("Node%d has deleted!\n\n", index);
}       

14、插入节点

由于过程有点复杂,请看下图:
插入节点过程图
第1段:设原来有n1、n2、n3共3个节点,其中每个节点包含“data”数据和“next”节点指针。n1的next指向下一个节点n2,n2的next指向下一个节点n3,n3的next指向NULL(因为它是最后一个节点);

第2段:若要将temp节点插入到n2节点与n3节点之间,第1步就是将n2节点的next(指向n3)赋值给temp的next,然后第2步将n2节点的next指向要插入的节点temp;

第3段:经过第1段和第2段后,插入了temp,它的最终链接关系(n1–>n2–>temp–>n3)

通过index在节点之后插入一个节点,并将数据设置为data

void InsertNode(uint index, int data)
{
    Node *newNode = CreateNode(data);
    Node *insertNode = FindNode(index);

    if (headNode == NULL)
    {
        printf("链表为空!\n");
        return;
    }
    else if (insertNode == NULL)
    {
        printf("无此节点!\n");
        return;
    }
    else
    {
        // 将新节点的next 设置为 当前插入位置节点 的 下一个节点
        newNode->next = insertNode->next; 
        
        // 将 当前插入位置节点的next 设置为 newNode
        insertNode->next = newNode;
    }
}      
  • 48
    点赞
  • 256
    收藏
    觉得还不错? 一键收藏
  • 10
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值