C语言-单向不循环链表的基本操作(增、删、改、查)

一、前言

单向不循环链表作为链表中最简单的存在,比较适合才接触链表时进行学习,以下所有的代码都有详细注释以及可以在visual stdio中跑通。

二、详细代码

1、定义链表的节点以及包含用到头文件

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

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

2、创建新节点

Node* create_node(int val)
{
    Node* new_node = (Node*)malloc(sizeof(Node));
    if (!new_node)
    {
        printf("malloc error!");
        return NULL;
    }
    new_node->data = val;
    new_node->next = NULL;
    return new_node;
}

3、打印链表

void list_print(Node* head)
{
    Node* cur = head;
    printf("List: ");
    while (cur)
    {
        printf("%d -> ", cur->data);
        cur = cur->next;
    }
    printf("NULL\n");
}

4、头插

void insert_head(Node** head, int val)
{
    Node* new_node = create_node(val);
    new_node->next = *head;
    *head = new_node;
}

5、尾插

void insert_tail(Node** head, int val)
{
    Node* new_node = create_node(val);
    /* 空链表 */
    if (*head == NULL)
    {
        *head = new_node;
        return;
    }
    /* 非空链表 */
    Node* tail = *head;
    while (tail->next)
        tail = tail->next;

    tail->next = new_node;
}

6、任意位置插入,从0开始;返回0成功,返回-1失败

int insert_list(Node** head, int pos, int val)
{
    /* 位置小于0,直接返回失败 */
    if (pos < 0)	return -1;

    /* 位置为0,相当于头插 */
    if (pos == 0)
    {
        insert_head(head, val);
        return 0;
    }

    /* 找到插入位置的前驱节点 */
    Node* cur = *head;
    for (int i = 0; cur && i < pos - 1; i++)
        cur = cur->next;
    /* 处理pos过大以及空链表的情况 */
    if (!cur)	return -1;

    Node* new_node = create_node(val);
    new_node->next = cur->next;
    cur->next = new_node;
    return 0;
}

7、删除第一个值为val的节点,返回0成功,返回-1失败

int list_delete_val(Node** head, int val)
{
    if (*head == NULL)	return -1;
    /* 处理头节点 */
    if ((*head)->data == val)
    {
        Node* temp = *head;
        *head = (*head)->next;
        free(temp);
        return 0;
    }
    /* 处理非头节点 */
    Node* cur = *head, * prev = NULL;
    while (cur && cur->data != val)
    {
        prev = cur;
        cur = cur->next;
    }
    /* 没找到 */
    if (cur == NULL)	return -1;

    prev->next = cur->next;
    free(cur);
    return 0;
}

8、删除指定位置节点,pos从0开始,返回0成功,返回-1失败

int list_delete_pos(Node** head, int pos)
{
    /* 排除链表为空或者位置错误情况 */
    if (*head == NULL || pos < 0)	return -1;
    /* 处理头节点 */
    if (pos == 0)
    {
        Node* temp = *head;
        *head = (*head)->next; // 此处已保证 *head 非 NULL
        free(temp);
        return 0;
    }
    /* 处理非头节点 */
    Node* cur = *head, * prev = NULL;
    for (int i = 0; cur && i < pos; i++)
    {
        prev = cur;
        cur = cur->next;
    }
    /* pos越界 */
    if (cur == NULL)	return -1;
    prev->next = cur->next;
    free(cur);
    return 0;
}

9、把查找的第一个值替换为新的值,返回0成功,返回-1失败

int list_replace_val(Node* head, int oldval, int newval)
{
    Node* cur = head;
    while (cur && cur->data != oldval)
        cur = cur->next;

    if (cur == NULL) return -1;
    cur->data = newval;
    return 0;
}

10、把当前位置的值替换为新的值,返回0成功,返回-1失败

int list_find_pos(Node* head, int pos, int val)
{
    Node* cur = head;
    for (int i = 0; cur && i < pos; i++)
        cur = cur->next;

    if (cur == NULL) return -1;
    cur->data = val;
    return 0;
}

11、销毁整个链表

void list_destroy(Node** head)
{
    Node* cur = *head;
    while (cur)
    {
        Node* next = cur->next;
        free(cur);
        cur = next;
    }
    *head = NULL;
}

12、测试函数

int main()
{
    Node* head = NULL;

    /* 增 */
    insert_head(&head, 10);
    insert_head(&head, 20);
    insert_tail(&head, 50);
    insert_list(&head, 1, 15);
    list_print(head);

    /* 改 */
    list_replace_val(head, 20, 60);
    list_find_pos(head, 1, 1);
    list_print(head);

    /* 删 */
    list_delete_val(&head, 15);
    list_delete_pos(&head, 1);
    list_print(head);

    /* 销毁 */
    list_destroy(&head);
    return 0;
}

三、额外补充

为什么有的函数用了二重指针,有的函数没有用二重指针
因为使用了二重指针的函数都有可能操作到“链表头”,使用了二重指针就可以找到“链表头”的真正地址,否则调用者手里的head就永远停留在旧地址

posted @ 2026-01-08 10:51  tanyee  阅读(3)  评论(0)    收藏  举报