单链表增删改查的单独函数封装

一、单链表基础概念

1.定义

单链表是一种常见的数据结构,它由一系列节点组成,每个节点包含一个数据域和一个指向下一个节点的指针域。通过指针将各个节点连接起来,形成一个单链表。
在这里插入图片描述

二、单链表操作

1.定义节点结构体

typedef struct ListNode {
    int data;
    struct ListNode *next;
} ListNode;

定义了一个名为 ListNode 的结构体,它包含两个成员:一个是存储数据的整型类型 data,另一个是指向下一个节点的指针类型 next,通过这个指针可以将多个节点逐个连接起来形成单链表。

typedef语句定义新类型名:struct ListNode->ListNode:使代码更简洁明了,引用数据类型别名可增强程序的通用性、灵活性、可读性、可移植性。

2.创建新节点函数

ListNode *createNode(int value) {
    ListNode *newNode = (ListNode *)malloc(sizeof(ListNode));
    if (newNode == NULL) {
        printf("error!\n");
        return NULL;
    }
    
    newNode->data = value;
    newNode->next = NULL;
    
    return newNode;
}

这个函数接受一个整型参数 value,用于创建一个新的 ListNode 节点。首先通过 malloc 函数为节点分配内存空间,如果分配失败则输出提示信息并返回 NULL。然后将传入的 value 赋值给新节点的 data 成员,并将 next 成员初始化为 NULL,最后返回创建好的新节点指针。

malloc是C语言中的库函数,要引用头文件#include <stdlib.h>

3.创建单链表函数(尾插法)

ListNode *createLinkedList() {
    ListNode *head = NULL;
    ListNode *tail = NULL;
    
    int value;
    printf("输入 -1 结束:");
    scanf("%d", &value);

    while (value != -1) {
        ListNode *newNode = createNode(value);
        if (head == NULL) {
            head = newNode;
            tail = newNode;
        } 
        else {
            tail->next = newNode;
            tail = newNode;
        }

        scanf("%d", &value);
    }

    return head;
}

该函数用于创建一个单链表,采用尾插法,即每次在链表的尾部添加新节点。首先初始化链表的头指针 head 和尾指针 tail 都为 NULL。然后通过循环不断读取用户输入的值,只要输入的值不等于 -1,就创建一个新节点并将其添加到链表的尾部。如果链表为空(即 headNULL),则新节点既是头节点也是尾节点;否则,将新节点添加到尾节点的后面,并更新尾节点指针。最后返回创建好的链表的头指针。

在这里插入图片描述

4.在链表头部插入节点函数

void insertAtHead(ListNode **head, int value) {
    ListNode *newNode = createNode(value);
    
    if (newNode == NULL) {
    return;
    }
    newNode->next = *head;
    *head = newNode;
}

这个函数接受一个指向链表头指针的指针 head 和一个整数参数 value,用于在链表的头部插入一个新节点。首先创建一个新节点,然后将新节点的 next 指针指向原来的头节点,最后将新节点设置为新的头节点,即通过修改 *head 来更新链表的头指针。

在这里插入图片描述

5.在链表指定位置插入节点函数

void insertAtPosition(ListNode **head, int position, int value) {
    if (position == 0) {
        insertAtHead(head, value);
        return;
    }

    ListNode *prev = NULL;
    ListNode *curr = *head;
    int i = 0;

    while (curr!= NULL && i < position) {//这里curr不可写成curr->next,否则少判多一个节点。
        prev = curr;
        curr = curr->next;
        i++;
    }

    if (curr == NULL && i < position) {
        printf("指定位置超出链表长度!\n");
        return;
    }

    ListNode *newNode = createNode(value);
    prev->next = newNode;
    newNode->next = curr;
}

此函数用于在链表的指定位置插入一个新节点。首先判断如果要插入的位置是 0,则调用 insertAtHead 函数在头部插入节点。否则,通过循环遍历链表找到要插入位置的前一个节点 prev 和要插入位置的节点 curr。如果在遍历完链表后还没有找到指定位置(即 currNULLi 小于 position),则输出提示信息并返回。找到合适位置后,创建一个新节点并将其插入到 prevcurr 之间,即先将 prevnext 指针指向新节点,再将新节点的 next 指针指向 curr

在这里插入图片描述

6.删除链表头部节点函数

void deleteAtHead(ListNode **head) {
    if (*head == NULL) {
        printf("链表为空,无法删除头部节点!\n");
        return;
    }

    ListNode *temp = *head;
    *head = (*head)->next;
    free(temp);
}

该函数用于删除链表的头部节点。首先判断链表是否为空,如果为空则输出提示信息并返回。否则,先保存当前头节点的指针到 temp 变量,然后将链表的头指针更新为原来头节点的下一个节点(即 *head = (*head)->next),最后通过 free 函数释放原来头节点所占用的内存。

7.删除链表指定位置节点

void deleteAtPosition(ListNode **head, int position) {
    if (position == 0) {
        deleteAtHead(head);
        return;
    }

    ListNode *prev = NULL;
    ListNode *curr = *head;
    int i = 0;

    while (curr!= NULL && i < position) {
        prev = curr;
        curr = curr->  next;
        i++;
    }

    if (curr == NULL) {
        printf("指定位置超出链表长度!\n");
        return;
    }

    prev->next = curr->next;
    free(curr);
}

这个函数用于删除链表指定位置的节点。首先判断如果要删除的位置是 0,则调用 deleteAtHead 函数删除头部节点。否则,通过循环遍历链表找到要删除位置的前一个节点 prev 和要删除位置的节点 curr。如果在遍历完链表后还没有找到指定位置(即 currNULL),则输出提示信息并返回。找到合适位置后,将 prevnext 指针指向 curr 的下一个节点(即 prev->next = curr->next),从而跳过 curr 节点,然后通过 free 函数释放 curr 节点所占用的内存。

在这里插入图片描述

8.修改链表指定位置函数值

void modifyAtPosition(ListNode *head, int position, int new_value) {
    ListNode *curr = head;
    int i = 0;

    while (curr!= NULL && i < position) {
        curr = curr->next;
        i++;
    }

    if (curr == NULL) {
        printf("指定位置超出链表长度!\n");
        return;
    }

    curr->data = new_value;
}

此函数用于修改链表指定位置节点的值。通过循环遍历链表找到要修改位置的节点 curr,如果在遍历完链表后还没有找到指定位置(即 currNULL),则输出提示信息并返回。找到合适位置后,将该节点的 data 成员的值更新为传入的新值 new_value

9.查找链表指定位置节点

ListNode *findAtPosition(ListNode *head, int position) {
    ListNode *curr = head;
    int i = 0;

    while (curr!= NULL && i < position) {
        curr = curr->next;
        i++;
    }

    if (i == position) {
        printf("找到了!");
        return curr;
    } 
    else {
        printf("没找到!");
        return NULL;
    }
}

此函数用于查找表指定位置节点。通过循环遍历链表找到要查找的位置的节点 curr,如果在遍历完链表后还没有找到指定位置(即 currNULL),则输出提示信息并返回。找到合适位置后,输出提示信息。

10.释放链表内存

void freeLinkedList(ListNode *head) {
    ListNode *temp;
    while (head!= NULL) {
        temp = head;
        head = head->next;
        free(temp);
    }
}

这个函数用于释放整个链表所占用的内存。通过循环遍历链表,每次先保存当前节点的指针到 temp 变量,然后将链表头指针更新为下一个节点(即 head = head->next),最后通过 free 函数释放 temp 所指向的节点所占用的内存,直到链表为空(即 headNULL)为止。

11.打印链表函数

void printLinkedList(ListNode *head) {
    ListNode *curr = head;
    while (curr!= NULL) {
        printf("%d ", curr->data);
        curr = curr->next;
    }
    printf("\n");
}

该函数用于打印链表中所有节点的数据值。通过循环遍历链表,从链表头节点开始,每次输出当前节点的 data 成员的值,然后将指针移动到下一个节点,直到链表为空(即 currNULL)为止,最后输出一个换行符。

三.全部代码并main函数测验

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

// 定义单链表节点结构体
typedef struct ListNode {
    int data;
    struct ListNode *next;
} ListNode;

// 创建新节点函数
ListNode *createNode(int value) {
    ListNode *newNode = (ListNode *)malloc(sizeof(ListNode));
    if (newNode == NULL) {
        printf("内存分配失败!\n");
        return NULL;
    }
    newNode->data = value;
    newNode->next = NULL;
    return newNode;
}

// 创建单链表函数(尾插法)
ListNode *createLinkedList() {
    ListNode *head = NULL;
    ListNode *tail = NULL;
    int value;

    printf("输入 -1 结束:");
    scanf("%d", &value);

    while (value!= -1) {
        ListNode *newNode = createNode(value);
        if (head == NULL) {
            head = newNode;
            tail = newNode;
        } else {
            tail->next = newNode;
            tail = newNode;
        }

        scanf("%d", &value);
    }

    return head;
}

// 在链表头部插入节点函数
void insertAtHead(ListNode **head, int value) {
    ListNode *newNode = createNode(value);
    if (newNode == NULL) {
        return;
    }
    newNode->next = *head;
    *head = newNode;
}

// 在链表指定位置插入节点函数
void insertAtPosition(ListNode **head, int position, int value) {
    if (position == 0) {
        insertAtHead(head, value);
        return;
    }

    ListNode *prev = NULL;
    ListNode *curr = *head;
    int i = 0;

    while (curr!= NULL && i < position) {
        prev = curr;
        curr = curr->next;
        i++;
    }

    if (curr == NULL && i < position) {
        printf("指定位置超出链表长度!\n");
        return;
    }

    ListNode *newNode = createNode(value);
    prev->next = newNode;
    newNode->next = curr;
}

// 删除链表头部节点函数
void deleteAtHead(ListNode **head) {
    if (*head == NULL) {
        printf("链表为空,无法删除头部节点!\n");
        return;
    }

    ListNode *temp = *head;
    *head = (*head)->next;
    free(temp);
}

// 删除链表指定位置节点函数
void deleteAtPosition(ListNode **head, int position) {
    if (position == 0) {
        deleteAtHead(head);
        return;
    }

    ListNode *prev = NULL;
    ListNode *curr = *head;
    int i = 0;

    while (curr!= NULL && i < position) {
        prev = curr;
        curr = curr->next;
        i++;
    }

    if (curr == NULL) {
        printf("指定位置超出链表长度!\n");
        return;
    }

    prev->next = curr->next;
    free(curr);
}

// 修改链表指定位置节点的值函数
void modifyAtPosition(ListNode *head, int position, int new_value) {
    ListNode *curr = head;
    int i = 0;

    while (curr!= NULL && i < position) {
        curr = curr->next;
        i++;
    }

    if (curr == NULL) {
        printf("指定位置超出链表长度!\n");
        return;
    }

    curr->data = new_value;
}

//查找链表指定位置节点
ListNode* findAtPosition(ListNode* head, int position) {
    ListNode* curr = head;
    int i = 0;

    while (curr != NULL && i < position) {
        curr = curr->next;
        i++;
    }

    if (i == position) {
        printf("找到了!");
            return curr;
    }
    else {
        printf("没找到!");
        return NULL;
    }
}

// 释放链表内存函数
void freeLinkedList(ListNode *head) {
    ListNode *temp;
    while (head!= NULL) {
        temp = head;
        head = head->next;
        free(temp);
    }
}

// 打印链表函数
void printLinkedList(ListNode *head) {
    ListNode *curr = head;
    while (curr!= NULL) {
        printf("%d ", curr->data);
        curr = curr->next;
    }
    printf("\n");
}

int main() {
    ListNode *head = createLinkedList();

    printf("原始链表:");
    printLinkedList(head);

    insertAtHead(&head, 100);
    printf("在头部插入节点100后的链表:");
    printLinkedList(head);

    insertAtPosition(&head, 2, 200);
    printf("在位置2插入节点200后的链表:");
    printLinkedList(head);

    deleteAtHead(&head);
    printf("删除头部节点后的链表:");
    printLinkedList(head);

    deleteAtPosition(&head, 1);
    printf("删除位置1节点后的链表:");
    printLinkedList(head);

    modifyAtPosition(head, 0, 300);
    printf("修改位置0节点值为300后的链表:");
    printLinkedList(head);
    
    printf("查找位置1节点链表:");
 	findAtPosition(head, 1);

    freeLinkedList(head);

    return 0;
}

main 函数中,首先调用 createLinkedList 函数创建一个单链表,然后依次调用各种操作函数对链表进行插入、删除、修改、查找等操作,并在每次操作后通过 printLinkedList 函数打印链表的当前状态,最后通过 freeLinkedList 函数释放链表所占用的内存,以避免内存泄漏。

在这里插入图片描述

四.结语

作为大一新生,对与C语言的学习任重道远,而链表又是重中之重,链表的学习是一件耗时持久的事,需要持之以恒,不断复习,希望自己能继续坚持、努力学习,也希望本篇文章对你有所帮助。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值