C语言对单链表所有操作与一些相关面试题

这篇具有很好参考价值的文章主要介绍了C语言对单链表所有操作与一些相关面试题。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

单链表的特性

单链表的所有操作

定义一个单链表

创建一个链表头

插入数据(头插法)

插入数据(尾插法)

查找节点

修改数据节点

删除节点

打印数据

销毁链表

翻转链表

打印链表长度

冒泡排序

快排

堆排

查找倒数第K个节点(双指针法)

完整测试代码


单链表的特性

单链表是一种线性数据结构,它由一系列的节点组成,每个节点包含一个数据域和一个指向下一个节点的指针域。单链表的特性有:文章来源地址https://www.toymoban.com/news/detail-705040.html

  • 单链表的长度是可变的,可以动态地插入和删除节点。
  • 单链表的访问是顺序的,要访问某个节点,必须从头节点开始遍历,直到找到该节点或者到达链表尾部。
  • 单链表不需要连续的内存空间,可以利用零散的空间存储数据。
  • 单链表的优势是:
    • 插入和删除操作比较简单,只需要修改指针域即可,不需要移动其他节点。
    • 单链表可以实现一些特殊的功能,如栈、队列、循环链表等。
  • 单链表的劣势是:
    • 访问操作比较慢,需要遍历整个链表,时间复杂度为O(n)。
    • 单链表需要额外的空间存储指针域,增加了空间开销。
    • 单链表容易产生内存碎片,如果频繁地插入和删除节点,可能导致内存不连续。

单链表的所有操作

定义一个单链表
// 声明并定义个单链表结构体
typedef struct _ListNode
{
    int val;                //数据 成员变量
    struct _ListNode * next;//结构体调用自己的类型
}ListNode;
创建一个链表头
void listCreate(ListNode *node)
{
    //初始化链表内数据
    node->val = -1;
    node->next = NULL;
}
插入数据(头插法)
void listInsert(ListNode *node, int data)
{
    // 创建一个节点,并申请内存
    ListNode *t_node = (ListNode *)malloc(sizeof(ListNode));

    //  节点内容赋值
    t_node->val = data;
 
    //   头插法,新数据在前
    t_node->next = node->next;
    node->next = t_node;
}
插入数据(尾插法)
void listTailInsert(ListNode *node, int data)
{
    // 创建一个节点
    ListNode *t_node = (ListNode*)malloc(sizeof(ListNode));
    
    // 节点内容赋值
    t_node->val = data;
    t_node->next = NULL;
    
    // 声明一个尾节点
    ListNode* t_tail = node;

    // 获取最后一个节点
    while(t_tail->next != NULL)
    {
        // 后移
        t_tail = t_tail->next;
    }
    //添加节点
    t_tail->next = t_node;
}
查找节点
ListNode* listFind(ListNode *node, int data)
{
    //申明一个空节点
    ListNode *t_node = NULL;
    
    //遍历链表
    ListNode *t_temp;
    for(t_temp = node->next; t_temp != NULL; t_temp = t_temp->next)
    {
        //如果找到该节点
        if(t_temp->val == data)
        {
            t_node = t_temp;
            //跳出循环
            break;
        }
    }
    return t_node;
}
修改数据节点
void listModify(ListNode *node, int oldData, int newData)
{
    // 查找值是否存在
    ListNode *t_node = listFind(node, oldData);
    
    // 判断值是否存在
    if(t_node == NULL)
    {
        printf("该值不存在\n");
        return;
    }
    t_node->val = newData;
}
删除节点
void listDelete(ListNode *node, int data)
{
    // 查找是否存在改制的数据
    ListNode *t_node = listFind(node, data);
    
    // 如果该值对应的节点不存在
    if(NULL == t_node)
    {
        printf("该值不存在\n");
        return;
    }

    // 求出被删节点的前一个节点
    ListNode *t_prev = node;
    
    // 遍历链表
    while(t_prev->next != t_node)
    {
        t_prev = t_prev->next;
    }

    // 前一个节点的next指向被删除节点的下一个节点
    t_prev->next = t_node->next;
    // 释放内存
    free(t_node);
    // 指针置空
    t_node = NULL;
}
打印数据
void listDisplay(ListNode *node)
{
    // 遍历链表
    ListNode *t_temp;
    
    for(t_temp = node->next; t_temp != NULL; t_temp = t_temp->next)
    {
        printf("%d ",t_temp->val);
    }
    printf("\n");
}
销毁链表
void listDestroy(ListNode *node)
{
    // 遍历链表
    ListNode *t_temp = node->next;
    
    while(t_temp != NULL)
    {
        // 先将当前节点保存
        ListNode *t_node = t_temp;
        // 移动到下一各节点
        t_temp = t_temp->next;
        // 释放保存内容的节点
        free(t_node);
    }
}
翻转链表
void listReverse(ListNode *node)
{
    ListNode * head = NULL, *now = NULL, *temp = NULL;
    head = node->next;
    // head是来保存我们翻转以后链表中的头节点的
    now = head->next;
    // now用来保存我们当前待处理的节点
    head->next = NULL;
    // 一定要置为NULL,否则可能导致循环
 
    while(now)
    {
        temp = now->next; // 利用一个临时指针来保存下一个待处理的节点
        
        now->next = head; // 将当前节点插入到逆序节点的第一个节点之前,并更改head指向
        
        head = now;
        node->next = head; // 使链表头指针指向逆序后的第一个节点
        
        now = temp; // 更新链表到下一个待处理的节点
    }
}
打印链表长度
int listLength(ListNode *node)
{
    ListNode *t_temp;
    int t_length = 0;
    for(t_temp = node->next; t_temp != NULL; t_temp = t_temp->next)
    {
        t_length++;
    }
    return t_length;
}
冒泡排序
void listBubbleSort(ListNode *node)
{
    int t_length = listLength(node);
    int i,j;
    ListNode *t_temp;
    for(i = 0; i < t_length; i++)
    {
        t_temp = node->next;
        for(j = 0;j < t_length - i - 1; j++)
        {
            if(t_temp->val > t_temp->next->val)
            {
                int t_data = t_temp->val;
                t_temp->val = t_temp->next->val;
                t_temp->next->val = t_data;
            }
            t_temp = t_temp->next;
        }
    }
}
快排
void quickSort(struct _ListNode *head, struct _ListNode *tail) {
    // 如果链表为空或只有一个节点,直接返回
    if (head == NULL || head == tail) return;
    // 定义两个指针p和q,用于分割链表
    struct _ListNode *p = head, *q = head->next;
    // 选取第一个节点作为基准值
    int pivot = head->val;
    // 遍历链表,将小于基准值的节点放到p的后面
    while (q != tail->next) {
        if (q->val < pivot) {
            p = p->next;
            // 交换p和q指向的节点的值
            int temp = p->val;
            p->val = q->val;
            q->val = temp;
        }
        q = q->next;
    }
    // 交换head和p指向的节点的值,使得p指向的节点为基准值
    int temp = head->val;
    head->val = p->val;
    p->val = temp;
    // 对左右两部分递归进行快速排序
    quickSort(head, p);
    quickSort(p->next, tail);
}
堆排
// 待实现
查找倒数第K个节点(双指针法)
ListNode* listFindKthToTail(ListNode *node, int k)
{
    // 超过长度直接返回空
    if(node == NULL || k >= listLength(node))return NULL;

    ListNode *first = node, *second = node;
    for(int i = 0; i < k; i++){
        first = first->next;
    }

    while (first)
    {
        first = first->next;
        second = second->next;
    }
    return second;
}

完整测试代码

#include <stdio.h>
#include <stdlib.h>
 
// 声明并定义个单链表结构体
typedef struct _ListNode
{
    int val;                //数据 成员变量
    struct _ListNode * next;//结构体调用自己的类型
}ListNode;
 
 
/**
 * 创建链表
*/
void listCreate(ListNode *node)
{
    //初始化链表内数据
    node->val = -1;
    node->next = NULL;
}
 
/**
 * 插入数据,头插法
*/
void listInsert(ListNode *node, int data)
{
    // 创建一个节点,并申请内存
    ListNode *t_node = (ListNode *)malloc(sizeof(ListNode));

    //  节点内容赋值
    t_node->val = data;
 
    //   头插法,新数据在前
    t_node->next = node->next;
    node->next = t_node;
}

/**
 * 插入数据,尾插法
*/
void listTailInsert(ListNode *node, int data)
{
    // 创建一个节点
    ListNode *t_node = (ListNode*)malloc(sizeof(ListNode));
    
    // 节点内容赋值
    t_node->val = data;
    t_node->next = NULL;
    
    // 声明一个尾节点
    ListNode* t_tail = node;

    // 获取最后一个节点
    while(t_tail->next != NULL)
    {
        // 后移
        t_tail = t_tail->next;
    }
    //添加节点
    t_tail->next = t_node;
}

/**
 * 查找数据
*/
ListNode* listFind(ListNode *node, int data)
{
    //申明一个空节点
    ListNode *t_node = NULL;
    
    //遍历链表
    ListNode *t_temp;
    for(t_temp = node->next; t_temp != NULL; t_temp = t_temp->next)
    {
        //如果找到该节点
        if(t_temp->val == data)
        {
            t_node = t_temp;
            //跳出循环
            break;
        }
    }
    return t_node;
}
 
/**
 * 修改数据
*/
void listModify(ListNode *node, int oldData, int newData)
{
    // 查找值是否存在
    ListNode *t_node = listFind(node, oldData);
    
    // 判断值是否存在
    if(t_node == NULL)
    {
        printf("该值不存在\n");
        return;
    }
    t_node->val = newData;
}
 
/**
 * 删除数据
*/
void listDelete(ListNode *node, int data)
{
    // 查找是否存在改制的数据
    ListNode *t_node = listFind(node, data);
    
    // 如果该值对应的节点不存在
    if(NULL == t_node)
    {
        printf("该值不存在\n");
        return;
    }

    // 求出被删节点的前一个节点
    ListNode *t_prev = node;
    
    // 遍历链表
    while(t_prev->next != t_node)
    {
        t_prev = t_prev->next;
    }

    // 前一个节点的next指向被删除节点的下一个节点
    t_prev->next = t_node->next;
    // 释放内存
    free(t_node);
    // 指针置空
    t_node = NULL;
}
 
/**
 * 打印数据
*/
void listDisplay(ListNode *node)
{
    // 遍历链表
    ListNode *t_temp;
    
    for(t_temp = node->next; t_temp != NULL; t_temp = t_temp->next)
    {
        printf("%d ",t_temp->val);
    }
    printf("\n");
}
 
/**
 * 销毁链表
*/
void listDestroy(ListNode *node)
{
    // 遍历链表
    ListNode *t_temp = node->next;
    
    while(t_temp != NULL)
    {
        // 先将当前节点保存
        ListNode *t_node = t_temp;
        // 移动到下一各节点
        t_temp = t_temp->next;
        // 释放保存内容的节点
        free(t_node);
    }
}
 
 
/**
 * 翻转链表
*/
void listReverse(ListNode *node)
{
    ListNode * head = NULL, *now = NULL, *temp = NULL;
    head = node->next;
    // head是来保存我们翻转以后链表中的头节点的
    now = head->next;
    // now用来保存我们当前待处理的节点
    head->next = NULL;
    // 一定要置为NULL,否则可能导致循环
 
    while(now)
    {
        temp = now->next; // 利用一个临时指针来保存下一个待处理的节点
        
        now->next = head; // 将当前节点插入到逆序节点的第一个节点之前,并更改head指向
        
        head = now;
        node->next = head; // 使链表头指针指向逆序后的第一个节点
        
        now = temp; // 更新链表到下一个待处理的节点
    }
}
 
 
/**
 * 求长度
*/
int listLength(ListNode *node)
{
    ListNode *t_temp;
    int t_length = 0;
    for(t_temp = node->next; t_temp != NULL; t_temp = t_temp->next)
    {
        t_length++;
    }
    return t_length;
}
 
 
/**
 * 冒泡排序
*/
void listBubbleSort(ListNode *node)
{
    int t_length = listLength(node);
    int i,j;
    ListNode *t_temp;
    for(i = 0; i < t_length; i++)
    {
        t_temp = node->next;
        for(j = 0;j < t_length - i - 1; j++)
        {
            if(t_temp->val > t_temp->next->val)
            {
                int t_data = t_temp->val;
                t_temp->val = t_temp->next->val;
                t_temp->next->val = t_data;
            }
            t_temp = t_temp->next;
        }
    }
}

/**
 * 定义快速排序算法
*/
void quickSort(struct _ListNode *head, struct _ListNode *tail) {
    // 如果链表为空或只有一个节点,直接返回
    if (head == NULL || head == tail) return;
    // 定义两个指针p和q,用于分割链表
    struct _ListNode *p = head, *q = head->next;
    // 选取第一个节点作为基准值
    int pivot = head->val;
    // 遍历链表,将小于基准值的节点放到p的后面
    while (q != tail->next) {
        if (q->val < pivot) {
            p = p->next;
            // 交换p和q指向的节点的值
            int temp = p->val;
            p->val = q->val;
            q->val = temp;
        }
        q = q->next;
    }
    // 交换head和p指向的节点的值,使得p指向的节点为基准值
    int temp = head->val;
    head->val = p->val;
    p->val = temp;
    // 对左右两部分递归进行快速排序
    quickSort(head, p);
    quickSort(p->next, tail);
}

/**
 * 快速排序
*/
void listQuickSort(ListNode *node)
{
    ListNode *tail = node->next;
    while (tail->next)
    {
        tail = tail->next;
    }
    quickSort(node, tail);
}

/**
 * 堆排序
*/
void listHeapSort(ListNode *node)
{

}

/**
 * 获取链表倒数第k个节点,双指针方法
*/
ListNode* listFindKthToTail(ListNode *node, int k)
{
    // 超过长度直接返回空
    if(node == NULL || k >= listLength(node))return NULL;

    ListNode *first = node, *second = node;
    for(int i = 0; i < k; i++){
        first = first->next;
    }

    while (first)
    {
        first = first->next;
        second = second->next;
    }
    return second;
}

/**
 * 测试所有函数是否正确有效
*/
int main(int argc, char* argv[])
{
    //创建一个ListNode变量
    ListNode node;
 
    //创建链表
    listCreate(&node);
 
 
    int i = 0;
    for(i = 0;i < 10;i++)
    {
#if 0  
        listInsert(&node,i); // 插入数据头插法
#else
        listTailInsert(&node, i); // 插入数据尾插法
#endif
    }
    listDisplay(&node);

    ListNode* nodeFind = listFind(&node, 3);
    if(nodeFind)
        printf("listFind:%d\n", nodeFind->val);  

    const int k = 5;
    ListNode* nodeFindK = listFindKthToTail(&node, k);
    if(nodeFindK)
        printf("listFindKthToTail step:%d :%d\n", k, nodeFindK->val);  
    

    listModify(&node, 1, 999); //修改节点1为999
    listDisplay(&node);
    
    listDelete(&node, 5); // 删除节点5
    listDisplay(&node);
    
    
    // listBubbleSort(&node); // 冒泡排序
    listQuickSort(&node);    // quick sort
    listDisplay(&node);   // 打印链表数据
    
    listReverse(&node);       // 翻转链表
    
    listDisplay(&node);   // 打印反转后的链表
    listDestroy(&node);   // 销毁链表
    return 0;
}

到了这里,关于C语言对单链表所有操作与一些相关面试题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 单链表的基本操作代码实现(C语言版)

    目录 前言: 单链表的基本操作 准备工作(头文件、各种宏定义以及结构体定义) 一.较简单操作 1.单链表的初始化 2.判断单链表是否为空表 3.单链表的销毁 4.单链表的清空 5.求单链表的表长 二.较重要操作 1.单链表的取值 2.单链表元素的查找 3.单链表的结点插入 4.单链表的结

    2024年04月11日
    浏览(41)
  • 【数据结构】单链表的基本操作 (C语言版)

    目录 一、单链表 1、单链表的定义: 2、单链表的优缺点: 二、单链表的基本操作算法(C语言) 1、宏定义 2、创建结构体 3、初始化 4、插入 4、求长度 5、清空 6、销毁  7、取值 8、查找 9、删除 10、头插法创建单链表 11、尾插法创建单链表 三、单链表的全部代码(C语言)

    2024年01月22日
    浏览(60)
  • 【数据结构】C语言实现单链表的基本操作

    大家好,很高兴又和大家见面啦!!! 在上一篇中,我们详细介绍了单链表的两种创建方式——头插法与尾插法,相信大家现在对这两种方式都已经掌握了。今天咱们将继续介绍单链表的基本操作——查找、插入与删除。在开始今天的内容之前,我们先通过尾插法创建一个单

    2024年02月03日
    浏览(65)
  • 【想要安利给所有人的开发工具】最强工具ChatGPT——分享一些使用经验

    目录 🔥个人使用ChatGPT的经验 🔥如何使用ChatGPT  方法一 方法二 🔥🔥提问技巧分享  1、英语翻译员 2、面试官 3、javascript 控制台 4、Excel表格 5、作曲家 6、辩手 7、小说家 8、诗人 9、数学老师 10、网络安全专家 11、医生 12、统计员 13、占星师 14、机器学习工程师 15、R编程

    2024年01月20日
    浏览(52)
  • 记一次后台开发面试拷打过程

    开头简单的自我介绍,面试官和我聊了聊天缓解个人紧张状况,然后就让开屏幕共享开视频做题目,做完以后,问了一些问题,就让等通知了,估计是凉了,不过这里且把当时做的笔试题目复盘一下吧!题目是ai做的题解,唉,AI都比我强,比我面试的时候解释的强多了,未来

    2024年02月08日
    浏览(43)
  • 【数据结构】 循环单链表的基本操作 (C语言版)

    目录 一、循环单链表 1、循环单链表的定义: 2、循环单链表的优缺点: 二、循环单链表的基本操作算法(C语言)    1、宏定义  2、创建结构体 3、循环单链表的初始化  4、循环单链表的插入 5、求单链表长度 6、循环单链表的清空 7、循环单链表的销毁 8、循环单链表的取

    2024年01月22日
    浏览(60)
  • 数据结构和算法——用C语言实现所有图状结构及相关算法

    本文所有代码均在仓库中,这是一个完整的由纯C语言实现的可以存储任意类型元素的数据结构的工程项目。 首先是极好的工程意识,该项目是一个中大型的CMake项目,结构目录清晰,通过这个项目可以遇见许多工程问题并且可以培养自己的工程意识。 其次是优秀的封装性(

    2024年02月06日
    浏览(222)
  • C语言单链表实现初始化、创建、增、删、查等基本操作(详细)

    提示:文章参考王道的课程,相当于课程笔记 目录 一、单链表的定义及初始化 1、定义   2、初始化  1)不带头结点的单链表  2)带头节的单链表  二、单链表插入和删除 1)插入 1、按位序插入(带头结点) 2、按位插入(不带头结点)  3、指定结点的后插操作  4、指定结点

    2023年04月08日
    浏览(65)
  • 遍历所有控件,批量保存标签、批量操作编辑框,读取所有标签(易语言)

    这几天用易语言写一些工作上的数据显示小软件,因为软件上标签与编辑框较多,如果一 一去读取和保存的话,程序显得很冗长,并且扩展性不好,增加或删减1,2个控件,程序又得重新检查重写,而网上查了半天,关于易的批量操作控件,估计是太少人用吧,找不到。好吧

    2024年02月06日
    浏览(49)
  • 第1关:单循环链表的实现—链表的添加、遍历任务描述相关知识单循环链表添加操作遍历循环链表编程要求测试说明任务描述在操作单链表时,

    第1关:单循环链表的实现—链表的添加、遍历 200 任务要求 参考答案 评论42 任务描述 相关知识 单循环链表 添加操作 遍历循环链表 编程要求 测试说明 任务描述 在操作单链表时,我们有时希望从单链表中的任一结点出发都能遍历整个链表,但对于单链表来说,只有从头结点

    2024年02月06日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包