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

目录

单链表的特性

单链表的所有操作

定义一个单链表

创建一个链表头

插入数据(头插法)

插入数据(尾插法)

查找节点

修改数据节点

删除节点

打印数据

销毁链表

翻转链表

打印链表长度

冒泡排序

快排

堆排

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

完整测试代码


单链表的特性

单链表是一种线性数据结构,它由一系列的节点组成,每个节点包含一个数据域和一个指向下一个节点的指针域。单链表的特性有:

  • 单链表的长度是可变的,可以动态地插入和删除节点。
  • 单链表的访问是顺序的,要访问某个节点,必须从头节点开始遍历,直到找到该节点或者到达链表尾部。
  • 单链表不需要连续的内存空间,可以利用零散的空间存储数据。
  • 单链表的优势是:
    • 插入和删除操作比较简单,只需要修改指针域即可,不需要移动其他节点。
    • 单链表可以实现一些特殊的功能,如栈、队列、循环链表等。
  • 单链表的劣势是:
    • 访问操作比较慢,需要遍历整个链表,时间复杂度为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); // 插入数据头插法
#elselistTailInsert(&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为999listDisplay(&node);listDelete(&node, 5); // 删除节点5listDisplay(&node);// listBubbleSort(&node); // 冒泡排序listQuickSort(&node);    // quick sortlistDisplay(&node);   // 打印链表数据listReverse(&node);       // 翻转链表listDisplay(&node);   // 打印反转后的链表listDestroy(&node);   // 销毁链表return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/74421.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

unix多进程多线程

概要 线程和进程是两种不同的并发执行单元&#xff0c;它们都可以实现多任务的执行&#xff0c;但是有各自的特点和区别。 线程是操作系统独立调度和执行的基本单位&#xff0c;它是进程的一个组成部分&#xff0c;一个进程可以包含多个线程&#xff0c;它们共享进程的地址空…

Microsoft Edge网页视频播放绿屏解决方法(B站)

一&#xff1a;问题&#xff0c;在B站观看视频时有绿色条纹 二&#xff1a;查找原因&#xff0c;未知 三&#xff1a;解决方法 三.1网页设置关闭硬件加速 三.2 点击视频播放下的 “小齿轮”&#xff0c;然后点击“更多播放设置” 把播放策略 “默认” 改为“AVC” 四&…

Tomcat配置域名和端口

Tomcat配置域名和端口 1.进入tomcat文件夹2. cd 到你的tomcat下3. 修改server.xml文件中监听端口4. 重启tomcat 1.进入tomcat文件夹 2. cd 到你的tomcat下 3. 修改server.xml文件中监听端口 继续修改server.xml中Host 4. 重启tomcat 进入bin ./shutdown.sh ./startup.sh …

etcd分布式存储

etcd分布式存储 etcd简介etcd下载安装etcd常用命令etcd配置参数etcd集群golang操作etcd

C++ map clear内存泄漏问题

map值存的是指针 map自带的clear()函数会清空map里存储的所有内容&#xff0c;但如果map值存储的是指针&#xff0c;则里面的值不会被清空&#xff0c;会造成内存泄漏&#xff0c;所以值为指针的map必须用迭代器清空。 使用erase迭代删除 迭代器删除值为指针的map&#xff0c…

rrweb入门

rrweb 背景 rrweb 是 record and replay the web&#xff0c;是当下很流行的一个录制屏幕的开源库。与我们传统认知的录屏方式&#xff08;如 WebRTC&#xff09;不同的是&#xff0c;rrweb 录制的不是真正的视频流&#xff0c;而是一个记录页面 DOM 变化的 JSON 数组&#x…

vue elementui 组合式 api 对于容器的滑动条的位置的获取与设置。切换页面可以保持原来的容器里的滑动条位置不变

需要使用 addEventListener 的方法获取滑动条的位置 xxx.vue 页面是一直缓存的&#xff0c;所以使用路由进入钩子&#xff08;onActivated&#xff09;设置滑动条的位置 App.vue: ...<el-container><router-view v-slot"{ Component }"><keep-alive…

Python基础篇(07):高阶函数lambda、zip、map、filter、reduce和函数注解

一、匿名函数 lambda 表达式 1、格式 lambda 参数: 表达式 冒号前是参数&#xff0c;可以有多个&#xff0c;用逗号隔开冒号右边的为表达式返回值是一个函数对象 2、举例&#xff1a;一个最简单的lambda函数 add lambda x, y: x y print(type(add)) # <class func…

Ubuntu 22.04 安装 Docker Engine

Install Docker Engine on Ubuntu | Docker Docs 比较方便的安装方式为通过 Apt Repo 来安装&#xff0c;需要三大步&#xff1a; 1. 预备仓库信息&#xff1a; 逐行执行一下命令 # Add Dockers official GPG key: sudo apt-get update sudo apt-get install ca-certificat…

R语言---使用runway进行机器学习模型性能的比较

R语言—使用runway进行机器学习模型性能的比较 #dataloadrm(list=ls())#librarylibrary(dcurves)library(gtsummary)library(tidyverse)library(mlr3verse)library(tidyverse)library(data.table)</

【鲁棒电力系统状态估计】基于投影统计的电力系统状态估计的鲁棒GM估计器(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

SpringCloud Alibaba 入门到精通 - Nacos

SpringCloud Alibaba 常用组件 一、基础结构搭建1.父工程创建2.子工程创建 二、Nacos&#xff1a;注册中心1.服务端搭建2.注册中心-客户端搭建3.注册中心-管理页面4.注册中心-常用配置5.注册中心-核心功能总结 三、Nacos注册中心集成Load Balancer 、OpenFeign1.Nacos客户端集成…

一键部署k8s集群

前置动作 关闭防火墙 systemctl disable firewalld && systemctl stop firewalld 关闭SELinux sed -i s#SELINUXenforcing#SELINUXdisabled#g /etc/selinux/config && grep SELINUXdisabled /etc/selinux/config setenforce 0 getenforce 关闭swap # 关闭…

前端面试题JS篇(4)

浏览器缓存 浏览器缓存分为强缓存和协商缓存&#xff0c;当客户端请求某个资源时&#xff0c;获取缓存的流程如下&#xff1a; 先根据这个资源的一些 http header 判断它是否命中强缓存&#xff0c;如果命中&#xff0c;则直接从本地获取缓存资源&#xff0c;不会发请求到服务…

c语言练习44:深入理解strstr

深入理解strstr strstr作用展示&#xff1a; #include <stdio.h> #include <string.h> int main() {char str[] "This is a simple string";char* pch;pch strstr(str, "simple");/*strncpy(pch, "sample", 6);*/printf("%s…

Android逆向学习(一)vscode进行android逆向修改并重新打包

Android逆向学习&#xff08;一&#xff09;vscode进行android逆向修改并重新打包 写在前面 其实我不知道这个文章能不能写下去&#xff0c;其实我已经开了很多坑但是都没填上&#xff0c;现在专利也发出去了&#xff0c;就开始填坑了&#xff0c;本坑的主要内容是关于androi…

Scrum认证高级Scrum Master (A-CSM) 认证培训课程

课程简介 高级ScrumMaster (Advanced Certified ScrumMaster, A-CSM) 认证课程是国际Scrum联盟推出的进阶级Scrum认证课程&#xff0c;是Scrum Master通往专业级敏捷教练必经的学习路径。 在ScrumMaster&#xff08;CSM&#xff09;认证课程中&#xff0c;您学习到了Scrum的价…

Centos 6.5 升级到Centos7指导手册

一、背景 某业务系统因建设较早&#xff0c;使用的OS比较过时&#xff0c;还是centos6.5的系统&#xff0c;因国产化需要&#xff0c;需将该系统升级到BClinux 8.6&#xff0c;但官方显示不支持centos 6.x升级到8&#xff0c;需先将centos6.5升级到centos7的最新版&#xff0c…

50道基础数据结构面试题

程序员必备的50道数据结构和算法面试题 在本文中&#xff0c;将分享一些常见的编程面试问题&#xff0c;这些问题来自于不同经验水平的程序员&#xff0c;囊括从刚大学毕业的人到具有一到两年经验的程序员。 编码面试主要包括数据结构和基于算法的问题&#xff0c;以及一些诸…

华为OD机试 - 战场索敌 - 深度优先搜索dfs算法(Java 2023 B卷 100分)

目录 一、题目描述二、输入描述三、输出描述四、深度优先搜索dfs五、解题思路六、Java算法源码七、效果展示1、输入2、输出3、说明4、如果增加目标敌人数量K为55、来&#xff0c;上强度 华为OD机试 2023B卷题库疯狂收录中&#xff0c;刷题点这里 一、题目描述 有一个大小是N*M…