单链表操作集合


前言

记录一下单链表的各种操作思路,方便后面复习回顾。

一、单链表介绍

链表是链式存储结构,用于存储逻辑关系为线性,也就是“一对一”关系的数据。

  • 有头链表:存在一个头节点,头节点的指针域有效数据域看个人
  • 无头链表:每一个数据域和指针域都有效。

无头链表

  • 特点:内存中不连续,通过指针链接。
  • 解决问题:长度固定的问题和插入删除麻烦的问题。
  • 逻辑结构:线性结构
  • 存储结构:链式存储结构
  • 操作:增删改查

二、单链表创键

为什么要用指针来操作链表呢?

在我看来是每一个节点都有指针来指向,所以自然想到用指针来操作了,而且我们开辟堆空间放节点时,也是用指针接收的。在对结构体进行操作时,我们通过移动头节点的指针来到达想要的位置,然后进行相应操作

1.初始化

  • 开辟空间,创建节点
  • 头节点数据域置0 (可选)
  • 头节点指针域指向NULL
// 初始化链表
Node* initList()
{
    // 开辟空间,创建节点
    Node* list = (Node*)malloc(sizeof(Node));
    list -> data = 0;   //头节点数据域一般存放节点个数
    list -> next = NULL;
    return list;
}
Node* list = initList();//头节点指针

2.头插法

在这里插入图片描述
头节点的后面插入头节点用来保存链表的信息

  • 开辟空间,创建节点
  • 数据域保存传进来的数据
  • 调整新建节点的指向 (上图1)
  • 调整头节点的指向 (上图2)
  • 头节点数据+1,表示新增了一个节点 (上图3, 可选)
// 头插法:从头节点的后面插入。头节点用来保存链表的信息
void headInsert(Node* list, int data)
{
    // 开辟空间
    Node* node = (Node*)malloc(sizeof(Node));
    node -> data = data;    //节点数据域保存传进来的数据
    node -> next = list -> next;    //将头节点next指向的节点地址赋值给新建节点next
    list -> next = node;    //头节点指向新建节点
    list -> data++;     //头节点数据+1,表示新增了一个节点
}

3.尾插法

在这里插入图片描述

// 尾插法:遍历到最后一个节点,在该节点后面插入新节点
void tailInsert(Node* list, int data)
{
    Node* head = list;
    // 开辟空间
    Node* node = (Node*)malloc(sizeof(Node));
    node -> data = data;
    node -> next = NULL;    //最后一个,没有可指向的节点
    //移动到最后一个节点
    while (list -> next)
    {
        list = list -> next; 
    }
    //最后节点指向新节点
    list -> next = node;
    head -> data++;//节点数量++    
}

4.打印

// 从第一个节点开始遍历输出每一个节点
void printList(Node* list)
{
    list = list -> next; //从第一个节点开始遍历  
    while (list)
    {
        printf("%d->", list -> data);
        list = list -> next;    
    }
    printf("\n");
}

5.删除

在这里插入图片描述

遍历所有节点, 将要删除的节点内容和每一个节点的内容进行比对,找到目标节点后,把它指向的节点传给前一个节点的next(如图1)

需要两个指针,初始时,一个(pre)指向头节点,一个(current)指向头节点后面,每次循环时,它们都向后移动一次
操作完要释放该空间(如图2)
头节点的data要-1(如图3)

void delete(Node* list, int data)
{
    Node* pre = list;
    Node* current = list->next;

    while (current)//遍历节点
    {
        if (current->data == data)//根据data找到指定位置
        {
            pre->next = current->next;//调整前一节点指向
            free(current);//释放空间
            current = NULL;
            printf("已删除!\n");
            list->data--;//节点数量-1
            break;
        }
        //移动到下一个节点
        pre = current;
        current = current->next;
    }
}

6.任意位置插入

// 任意位置插入:用户输入位置和数据
void insertNode(Node* list)
{
    int post;//插入位置
    int temp_data;//存用户的插入数
    printf("请输入要插入的位置:\n");
    scanf("%d", &post);
    getchar();
    // 进行容错判断
    if (post < 0 || post > list->data+1)//
    {
        printf("位置不在有效范围!\n");
        return;
    }

    printf("请输入要插入的数:\n");
    scanf("%d", &temp_data);
    getchar();

    // 开辟空间,创建节点
    Node* node = (Node*)malloc(sizeof(Node));
    node->data = temp_data;
    Node* current = list;//要移动的指针
    // 移动到要插入的前一个节点上
    for (int i = 1; i < post; i++)//插入到位1,不移动指针,插入到位2,移动1次,插入到位3,移动2次
    {
        current = current->next;
    }
    // 插入操作
    node->next = current->next;//调整插入节点的next
    current->next = node; //调整前一节点的next
    list->data++;
}

7.判断空表

// 判断是否空链表:看头节点的next是否为null
void isNullList(Node* list)
{
    if (list->next == NULL)
    {
        printf("NULL\n");
        return;
    }
    printf("节点数:%d\n", list->data);
}

8.清空节点

// 清空节点:从第一个节点开始遍历,将每一个节点进行删除,最后让头节点指向NULL
void noneNode(Node* list)
{
    Node* current = list->next;
    while (current != NULL)
    {
    	Node* current_next = current->next;//提前保存要删除节点的下一个节点位置
    	//删除操作
        free(current);
        current = NULL;
        list->data--;
        //当头节点data为0时,停止删除
        if (list->data == 0)
        {
            list->next = NULL;
            printf("已清空!\n");
            return;
        }
        current = current_next;//删除完,指针向后移动
    }
}

9.查找数据

// 查找:用户输入数据,将数据去和每一个节点进行匹配,如果成功,返回位置
void searchNode(Node* list)
{
    int temp_data;//要查找数据
    int num = 0;//记录数据所在位置
    printf("请输入要查找的数据:");
    scanf("%d", &temp_data);

    Node* current = list->next;//移动到第一个节点
    while (current != NULL)//遍历
    {
        if (current->data == temp_data)//匹配
        {
            printf("%d在位置%d!\n", temp_data, num+1);
        }
        num++;//不匹配位置++
        current = current->next;
    }
}

10.修改数据

//修改:让头节点指针移动到指点位置,修改节点的数据
void modifyNode(Node* list)
{
    int num;//位置
    int temp_data;//要修改的数据
    printf("请输入要修改的位置:");
    scanf("%d", &num);

    // 容错判断
    if (num < 0 || num > list->data)
    {
        printf("位置超范围!\n");
    }

    printf("请输入要修改的值:");
    scanf("%d", &temp_data);

    for (int i = 0; i < num; i++)//让头节点指针移动到修改位置
    {
        list = list->next;
    }

    list->data = temp_data;//修改
    printf("修改成功!\n");
}

11.数据转置

// 链表转置:将原表从头节点处拆开,遍历头节点后每一个节点,每次将要遍历的节点'头插入'头节点中
void linkListReverse(Node* list)
{
    
    Node* current = list->next;//原表用来移动的指针,从第一个节点开始
    list->next = NULL;
    
    while (current)//遍历原表
    {
        //将原表每一个节点头插入空的头节点
        Node* next_node = current->next;//在插入之前保存current下一个结点,不然current指向改变后,其后续节点找不到了
        current->next = list->next;//调整当前遍历节点的next
        list->next = current;//头节点指向新插入节点
        current = next_node;//移动到下一个节点
    }
    printf("转置完成!\n");
}

12.数据排序

// 排序
// 思路:
// 根据平均分降序排序
// 思路:将头节点和后续节点分开,对后续无头节点进行遍历,
// 将遍历节点重新插入头节点后面,每次插入时要考虑顺序
// 这样要插入的链表就是一个已经排序好的链表
void sortLinkList(Node* list)
{
    // 将头节点和后续节点分开前,要保存后续第一个节点位置,否找找不到后面
    Node* current = list->next;
    //将头节点和后续节点进行分开
    list->next = NULL;
//对后续无头节点进行遍历,将每个节点重新插入头节点后面,插入时考虑顺序
    while (current)
    {
        //每个节点插入前,都要提前保存下一个节点,因为后面current的next发生变化
        Node* current_next = current->next;
        //头节点指针复制品,用来移动的,指向头节点
        Node* sorted_ptr = list;
        //节点插入时,要考虑插入位置,当第一个位置不合适就去下一个位置,所以要对已排序链表进行遍历
        while (sorted_ptr)
        {
            // 什么时候进行插入?  
            //1.当已排序链表为空时直接插入(条件1) 
            //2.当遍历已排序链表最后位置时,直接插入(条件1) 
            //3.移到合适位置插入(条件2)
            if (sorted_ptr->next == NULL || current->data < sorted_ptr->data)
            {
                //头插操作
                current->next = sorted_ptr->next;
                sorted_ptr->next = current;
                break;
            }
            else
            {
                //不符合插入条件时指针后移
                sorted_ptr = sorted_ptr->next;
            }    
        }
        //发生插入后,current指针后移,移到下一个待插入节点
        current = current_next;
    }
    printf("排序完成!\n");
}

三 、全部代码

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

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

// 初始化
Node* initList()
{
    Node* list = (Node*)malloc(sizeof(Node));
    if (list == NULL)
    {
        perror("malloc error");
    }
    list->data = 0;
    list->next = NULL;
    return list;
}


// 头插法:从头节点的后面插入。头节点用来保存链表的信息
// void headInsert(Node* list, int data)
// {
//     // 开辟空间
//     Node* node = (Node*)malloc(sizeof(Node));
//     node -> data = data;    //节点数据域保存传进来的数据
//     node -> next = list -> next;    //将头节点next指向的节点地址赋值给新建节点next
//     list -> next = node;    //头节点指向新建节点
//     list -> data++;     //头节点数据+1,表示新增了一个节点
// }

// 尾插法
// void tailInsert(Node* list, int data)
// {
//     Node* head = list;
//     // 开辟空间
//     Node* node = (Node*)malloc(sizeof(Node));
//     node -> data = data;
//     node -> next = NULL;    //最后一个,没有可指向的节点
   
//     while (list -> next)
//     {
//         list = list -> next; 
//               //list已经是尾巴了,怎么让list->data加1呢? 
//     }
//     list -> next = node;
//     head -> data++;
// }

// 任意位置插入:用户输入位置和数据
void insertNode(Node* list)
{
    int post;//插入位置
    int temp_data;//存用户的插入数
    printf("请输入要插入的位置:\n");
    scanf("%d", &post);
    getchar();
    // 进行容错判断
    if (post < 0 || post > list->data+1)//
    {
        printf("位置不在有效范围!\n");
        return;
    }

    printf("请输入要插入的数:\n");
    scanf("%d", &temp_data);
    getchar();

    // 开辟空间,创建节点
    Node* node = (Node*)malloc(sizeof(Node));
    node->data = temp_data;
    Node* current = list;//要移动的指针
    // 移动到要插入的前一个节点上
    for (int i = 1; i < post; i++)//插入到1不移动指针,插入到2,移动1次,插入到3,移动2次
    {
        current = current->next;
    }
    // 插入操作
    node->next = current->next;//调整插入节点的next
    current->next = node; //调整前一节点的next
    list->data++;
}

// 删除
void delete(Node* list)
{
    int temp_data;
    printf("请输入要删除的数据:");
    scanf("%d", &temp_data);

    Node* pre = list;
    Node* current = list->next;

    while(current != NULL)
    {
        if (current->data == temp_data)
        {
            pre->next = current->next;
            free(current);
            current = NULL;
            list->data--;
            printf("删除成功!\n");
            return;
        }
        pre = current;
        current = current->next;
    }
    printf("没找到!\n");
}

// 判断是否空链表
void isNullList(Node* list)
{
    if (list->next == NULL)
    {
        printf("NULL\n");
        return;
    }
    printf("节点数:%d\n", list->data);
}

// 清空节点
void noneNode(Node* list)
{
    Node* current = list->next;

    while (current != NULL)
    {
        Node* hou = current->next;//用来保存移动指针的下一个节点位置
        free(current);
        current = NULL;
        list->data--;
        if (list->data == 0)
        {
            list->next = NULL;
            printf("已清空!\n");
            return;
        }
    
        current = hou;//当前指针向后移动
    }
}

// 查找:用户输入数据,如果存在,返回位置
void searchNode(Node* list)
{
    int temp_data;
    int num = 0;
    printf("请输入要查找的数据:");
    scanf("%d", &temp_data);

    Node* current = list->next;
    while (current != NULL)
    {
        if (current->data == temp_data)
        {
            printf("%d在位置%d!\n", temp_data, num+1);
        }
        num++;
        current = current->next;
    }
}

//修改数据
void modifyNode(Node* list)
{
    int num;
    int temp_data;
    printf("请输入要修改的位置:");
    scanf("%d", &num);

    // 容错判断
    if (num < 0 || num > list->data)
    {
        printf("位置超范围!\n");
    }

    printf("请输入要修改的值:");
    scanf("%d", &temp_data);

    for (int i = 0; i < num; i++)
    {
        list = list->next;
    }

    list->data = temp_data;
    printf("修改成功!\n");
}


// 链表转置:将原表从头节点处拆开,遍历头节点后每一个节点,每次将要遍历的节点'头插入'头节点中
void linkListReverse(Node* list)
{
    
    Node* current = list->next;//在断开前,保存后面第一个节点
    list->next = NULL;
    
    while (current)//遍历原表
    {
        //将原表每一个节点头插入空的头节点
        Node* next_node = current->next;//在插入之前保存current下一个结点,不然current指向改变后,其后续节点找不到了
        current->next = list->next;//调整当前遍历节点的next
        list->next = current;//头节点指向新插入节点
        current = next_node;//移动到下一个节点
    }
    printf("转置完成!\n");
}

// 排序
// 思路:
// 根据平均分降序排序
// 思路:将头节点和后续节点分开,对后续无头节点进行遍历,
// 将遍历节点重新插入头节点后面,每次插入时要考虑顺序
// 这样要插入的链表就是一个已经排序好的链表
void sortLinkList(Node* list)
{
    // 将头节点和后续节点分开前,要保存后续第一个节点位置,否找找不到后面
    Node* current = list->next;
    //将头节点和后续节点进行分开
    list->next = NULL;
//对后续无头节点进行遍历,将每个节点重新插入头节点后面,插入时考虑顺序
    while (current)
    {
        //每个节点插入前,都要提前保存下一个节点,因为后面current的next发生变化
        Node* current_next = current->next;
        //头节点指针复制品,用来移动的,指向头节点
        Node* sorted_ptr = list;
        //节点插入时,要考虑插入位置,当第一个位置不合适就去下一个位置,所以要对已排序链表进行遍历
        while (sorted_ptr)
        {
            // 什么时候进行插入?  
            //1.当已排序链表为空时直接插入(条件1) 
            //2.当遍历已排序链表最后位置时,直接插入(条件1) 
            //3.移到合适位置插入(条件2)
            if (sorted_ptr->next == NULL || current->data < sorted_ptr->data)
            {
                //头插操作
                current->next = sorted_ptr->next;
                sorted_ptr->next = current;
                break;
            }
            else
            {
                //不符合插入条件时指针后移
                sorted_ptr = sorted_ptr->next;
            }    
        }
        //发生插入后,current指针后移,移到下一个待插入节点
        current = current_next;
    }
    printf("排序完成!\n");
}

// 遍历
void printList(Node* list)
{
    list = list -> next;    //list++
    while (list)
    {
        printf("%d->", list -> data);
        list = list -> next;    //list++
    }
    printf("NULL\n");
}

int main(int argc, char const *argv[])
{
    Node* list = initList();

    isNullList(list);

    insertNode(list);
    insertNode(list);
    insertNode(list);
    insertNode(list);
    insertNode(list);
   
    // delete(list);
    printList(list);
    isNullList(list);

    

    // searchNode(list);
    modifyNode(list);
    printList(list);
    isNullList(list);
    // printf("节点数:%d\n", list->data);
    linkListReverse(list);
    printList(list);

    sortLinkList(list);
    printList(list);
    noneNode(list);
    isNullList(list);
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值